summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimon Rettberg2021-08-24 16:31:13 +0200
committerSimon Rettberg2021-08-24 16:31:13 +0200
commit664bc09f4f416df006e9ca1ce37d8dd93b8d3db7 (patch)
tree1b2f7064a053a00412f5d95d55fefda498d7b949
parent[eventlog] Check permissions; add synamic suggestions for keys (diff)
downloadslx-admin-664bc09f4f416df006e9ca1ce37d8dd93b8d3db7.tar.gz
slx-admin-664bc09f4f416df006e9ca1ce37d8dd93b8d3db7.tar.xz
slx-admin-664bc09f4f416df006e9ca1ce37d8dd93b8d3db7.zip
[statistics] Backup vorm urlaub
-rw-r--r--inc/crypto.inc.php3
-rw-r--r--inc/database.inc.php4
-rw-r--r--modules-available/statistics/api.inc.php17
-rw-r--r--modules-available/statistics/inc/hardwareinfo.inc.php328
-rw-r--r--modules-available/statistics/inc/parser.inc.php11
-rw-r--r--modules-available/statistics/install.inc.php41
6 files changed, 390 insertions, 14 deletions
diff --git a/inc/crypto.inc.php b/inc/crypto.inc.php
index 56f5073c..d3dd60dc 100644
--- a/inc/crypto.inc.php
+++ b/inc/crypto.inc.php
@@ -10,7 +10,8 @@ class Crypto
*/
public static function hash6($password)
{
- $salt = substr(str_replace('+', '.', base64_encode(pack('N4', mt_rand(), mt_rand(), mt_rand(), mt_rand()))), 0, 16);
+ $salt = substr(str_replace('+', '.',
+ base64_encode(pack('N4', mt_rand(), mt_rand(), mt_rand(), mt_rand()))), 0, 16);
$hash = crypt($password, '$6$' . $salt);
if (strlen($hash) < 60) Util::traceError('Error hashing password using SHA-512');
return $hash;
diff --git a/inc/database.inc.php b/inc/database.inc.php
index 09006f3e..48d8e3c6 100644
--- a/inc/database.inc.php
+++ b/inc/database.inc.php
@@ -375,9 +375,9 @@ class Database
* @param string $aiKey name of the AUTO_INCREMENT column
* @param array $uniqueValues assoc array containing columnName => value mapping
* @param array $additionalValues assoc array containing columnName => value mapping
- * @return int[] list of AUTO_INCREMENT values matching the list of $values
+ * @return int AUTO_INCREMENT value matching the given unique values entry
*/
- public static function insertIgnore($table, $aiKey, $uniqueValues, $additionalValues = false)
+ public static function insertIgnore($table, $aiKey, $uniqueValues, $additionalValues = false): int
{
// Sanity checks
if (array_key_exists($aiKey, $uniqueValues)) {
diff --git a/modules-available/statistics/api.inc.php b/modules-available/statistics/api.inc.php
index 04614521..9a81871c 100644
--- a/modules-available/statistics/api.inc.php
+++ b/modules-available/statistics/api.inc.php
@@ -1,5 +1,11 @@
<?php
+if (Request::any('action') === 'test' && isLocalExecution()) {
+ HardwareInfo::parseMachine('0A5D9E23-80F4-9C43-912C-96D80AE7E80B',
+ file_get_contents('/tmp/bla.json'));
+ exit;
+}
+
if (empty($_POST['type'])) die('Missing options.');
$type = mb_strtolower($_POST['type']);
@@ -62,7 +68,11 @@ if ($type[0] === '~') {
if (!is_string($hostname) || $hostname === $ip) {
$hostname = '';
}
- $data = Util::cleanUtf8(Request::post('data', '', 'string'));
+ $data = Util::cleanUtf8(Request::post('json', '', 'string'));
+ $hasJson = !empty($data);
+ if (!$hasJson) {
+ $data = Util::cleanUtf8(Request::post('data', '', 'string'));
+ }
// Prepare insert/update to machine table
$new = array(
'machineuuid'=> $uuid,
@@ -151,6 +161,10 @@ if ($type[0] === '~') {
$new['locationid'] = $loc; // For Filter Event
}
+ if ($hasJson) {
+ HardwareInfo::parseMachine($uuid, $data);
+ }
+
// Check for suspicious hardware changes
if ($old !== false) {
checkHardwareChange($old, $new);
@@ -316,6 +330,7 @@ if ($type[0] === '~') {
'hwid' => $hwid,
'machineuuid' => $uuid,
'devpath' => $port,
+ 'serial' => '',
), array('disconnecttime' => 0));
$validProps = array();
if (count($screen) > 1) {
diff --git a/modules-available/statistics/inc/hardwareinfo.inc.php b/modules-available/statistics/inc/hardwareinfo.inc.php
new file mode 100644
index 00000000..22c5a6f1
--- /dev/null
+++ b/modules-available/statistics/inc/hardwareinfo.inc.php
@@ -0,0 +1,328 @@
+<?php
+
+class HardwareInfo
+{
+
+ // Never change these!
+ const RAM_MODULE = 'RAM';
+ const MAINBOARD = 'MAINBOARD';
+ const DMI_SYSTEM = 'DMI_SYSTEM';
+ const POWER_SUPPLY = 'POWER_SUPPLY';
+ const SYSTEM_SLOT = 'SYSTEM_SLOT';
+ const PCI_DEVICE = 'PCI_DEVICE';
+
+
+ public static function parseMachine(string $uuid, string $data)
+ {
+ $data = json_decode($data, true);
+ $version = $data['version'] ?? 0;
+ if ($version != 2) {
+ error_log("Received unsupported hw json v$version");
+ return;
+ }
+ // determine misc stuff first
+ $mainboardExtra = [];
+ // physical memory array
+ $memArrays = self::getDmiHandles($data, 16);
+ $mainboardExtra['Memory Slot Count'] = 0;
+ $mainboardExtra['Memory Maximum Capacity'] = 0;
+ foreach ($memArrays as $mem) {
+ $mem = self::prepareDmiProperties($mem);
+ if (isset($mem['Number Of Devices']) && ($mem['Use'] ?? 0) === 'System Memory') {
+ $mainboardExtra['Memory Slot Count'] += $mem['Number Of Devices'];
+ }
+ if (isset($mem['Maximum Capacity'])) {
+ $mainboardExtra['Memory Maximum Capacity'] += Parser::convertSize($mem['Maximum Capacity'], 'M', false);
+ }
+ }
+ // Using the general helper function
+ $ramModCount = self::updateHwTypeFromDmi($uuid, $data, 17, self::RAM_MODULE, function (array $flat): bool {
+ return ($flat['Size'] ?? 0) > 65 * 1024 * 1024;
+ },
+ ['Locator'],
+ ['Data Width', 'Size', 'Form Factor', 'Type', 'Type Detail', 'Speed', 'Manufacturer', 'Part Number',
+ 'Minimum Voltage', 'Maximum Voltage'],
+ ['Locator', 'Bank Locator', 'Serial Number', 'Asset Tag', 'Configured Memory Speed', 'Configured Voltage']
+ );
+ // Fake RAM slots used/total into this
+ $mainboardExtra['Memory Slot Occupied'] = $ramModCount;
+ self::updateHwTypeFromDmi($uuid, $data, 2, self::MAINBOARD, ['Manufacturer', 'Product Name'],
+ [],
+ ['Manufacturer', 'Product Name', 'Type', 'Version'],
+ ['Serial Number', 'Asset Tag', 'Location In Chassis'],
+ $mainboardExtra
+ );
+ self::updateHwTypeFromDmi($uuid, $data, 1, self::DMI_SYSTEM, ['Manufacturer', 'Product Name'],
+ [],
+ ['Manufacturer', 'Product Name', 'Version', 'Wake-up Type'],
+ ['Serial Number', 'UUID', 'SKU Number']
+ );
+ self::updateHwTypeFromDmi($uuid, $data, 39, self::POWER_SUPPLY, ['Manufacturer'],
+ ['Location', 'Power Unit Group', 'Name'], // Location might ne empty/"Unknown", but Name can be something like "PSU 2"
+ ['Manufacturer', 'Product Name', 'Model Part Number', 'Revision', 'Max Power Capacity'],
+ ['Serial Number', 'Asset Tag', 'Status', 'Plugged', 'Hot Replaceable']
+ );
+ self::updateHwTypeFromDmi($uuid, $data, 9, self::SYSTEM_SLOT, function(array &$entry): bool {
+ if (!isset($entry['Type']))
+ return false;
+ // Split up PCIe info
+ if (preg_match('/^x(?<b>\d+) PCI Express( (?<g>\d+)( x(?<s>\d+))?)?$/', $entry['Type'], $out)) {
+ $entry['Type'] = 'PCI Express';
+ $entry['PCIe Bus Width'] = $out['b'];
+ if (!empty($out['g'])) {
+ $entry['PCIe Gen'] = $out['g'];
+ }
+ if (!empty($out['s'])) {
+ $entry['PCIe Slot Width'] = $out['s'];
+ }
+ }
+ return true;
+ },
+ ['Designation', 'ID', 'Bus Address'],
+ ['Type', 'PCIe Bus Width', 'PCIe Gen', 'PCIe Slot Width'],
+ ['Current Usage', 'Designation']
+ );
+ // ---- lspci ------------------------------------
+ $pciHwIds = [];
+ foreach (($data['lspci'] ?? []) as $dev) {
+ $hwid = self::writeGlobalHardwareData(self::PCI_DEVICE,
+ self::propsFromArray($dev, 'vendor', 'device', 'rev', 'class'));
+ $mappingId = self::writeLocalHardwareData($uuid, $hwid, $dev['slot'] ?? 'unknown',
+ self::propsFromArray($dev, 'slot', 'subsystem', 'subsystem_vendor'));
+ $pciHwIds[] = $mappingId;
+ }
+ self::markDisconnected($uuid, self::PCI_DEVICE, $pciHwIds);
+ // ---- Disks ------------------------------------
+ $hddHwIds = [];
+ foreach (($data['drives'] ?? []) as $dev) {
+ if (empty($dev['readlink']) || !isset($dev['smartctl']['device']))
+ continue;
+ $hwid = self::writeGlobalHardwareData(self::HDD, [
+ $dev['smartctl']['model_name'] ?? $dev['lsblk']['blockdevices'][0]['model'] ?? 'unknown',
+ $dev['lsblk']['blockdevices'][0]['size'] ?? 'unknown'
+ ]);
+ $mappingId = self::writeLocalHardwareData($uuid, $hwid, $dev['readlink'],
+ self::propsFromArray($dev['smartctl'] + ($dev['lsblk']['blockdevices'][0] ?? []),
+ 'serial_number', 'firmware_version', 'size'));
+ $hddHwIds[] = $mappingId;
+ }
+ }
+
+ private static function updateHwTypeFromDmi(string $uuid, array $data, int $type, string $dbType,
+ $requiredPropsOrCallback, array $pathFields, array $globalProps, array $localProps,
+ array $globalExtra = []): int
+ {
+ $sections = self::getDmiHandles($data, $type);
+ $thisMachineHwIds = [];
+ foreach ($sections as $section) {
+ $flat = self::prepareDmiProperties($section);
+ if (is_array($requiredPropsOrCallback)) {
+ foreach ($requiredPropsOrCallback as $prop) {
+ if (!isset($flat[$prop]))
+ continue 2;
+ }
+ }
+ if (is_callable($requiredPropsOrCallback)) {
+ if (!$requiredPropsOrCallback($flat))
+ continue;
+ }
+ $hwid = self::writeGlobalHardwareData($dbType, self::propsFromArray($flat, ...$globalProps) + $globalExtra);
+ $pathId = md5(self::idFromArray($flat, ...$pathFields));
+ $props = self::propsFromArray($flat, ...$localProps);
+ $mappingId = self::writeLocalHardwareData($uuid, $hwid, $pathId, $props);
+ $thisMachineHwIds[] = $mappingId;
+ }
+ // Any hw <-> client mappings not in that list get marked as disconnected
+ self::markDisconnected($uuid, $dbType, $thisMachineHwIds);
+ return count($thisMachineHwIds);
+ }
+
+ private static function writeGlobalHardwareData(string $dbType, array $global): int
+ {
+ static $cache = [];
+ // Since the global properties are supposed to be unique for a specific piece of hardware, use them all
+ // to generate a combined ID for this hardware entity, as opposed to $localProps, which should differ
+ // between instances of the same hardware entity, e.g. one specific HDD model has different serial numbers.
+ $id = md5(implode(' ', $global));
+ if (!isset($cache[$id])) {
+ // Cache lookup, make sure we insert this only once for every run, as this is supposed to be general
+ // information about the hardware, e.g. model number, max. resultion, capacity, ...
+ $cache[$id] = self::updateHwEntity($dbType, $id, $global);
+ }
+ return $cache[$id];
+ }
+
+ private static function markDisconnected(string $uuid, string $dbType, array $excludedHwIds)
+ {
+ error_log("Marking disconnected for $dbType from " . implode(', ', $excludedHwIds));
+ if (empty($excludedHwIds)) {
+ Database::exec("UPDATE machine_x_hw mxh, statistic_hw h
+ SET mxh.disconnecttime = UNIX_TIMESTAMP()
+ WHERE h.hwtype = :type AND h.hwid = mxh.hwid AND mxh.machineuuid = :uuid
+ AND mxh.disconnecttime = 0",
+ ['type' => $dbType, 'uuid' => $uuid]);
+ } else {
+ Database::exec("UPDATE machine_x_hw mxh, statistic_hw h
+ SET mxh.disconnecttime = UNIX_TIMESTAMP()
+ WHERE h.hwtype = :type AND h.hwid = mxh.hwid AND mxh.machineuuid = :uuid
+ AND mxh.disconnecttime = 0 AND mxh.machinehwid NOT IN (:hwids)",
+ ['type' => $dbType, 'uuid' => $uuid, 'hwids' => $excludedHwIds]);
+ }
+ }
+
+ /**
+ * Takes key-value-array, returns a new array with only the keys listed in $fields.
+ */
+ private static function propsFromArray(array $array, string ...$fields): array
+ {
+ $ret = [];
+ foreach ($fields as $field) {
+ if (isset($array[$field])) {
+ $ret[$field] = $array[$field];
+ }
+ }
+ return $ret;
+ }
+
+ /**
+ * Takes key-value-array, returns a concatenated string of all the values with the keys given in $fields.
+ * The items are separated by spaces, and returned in the order they were given in $fields. Missing keys
+ * are silently omitted.
+ */
+ private static function idFromArray(array $array, string ...$fields): string
+ {
+ $out = '';
+ foreach ($fields as $field) {
+ if (!isset($array[$field]))
+ continue;
+ if (empty($out)) {
+ $out = $array[$field];
+ } else {
+ $out .= ' ' . $array[$field];
+ }
+ }
+ return $out;
+ }
+
+ private static function updateHwEntity(string $hwType, string $uniqueName, array $props): int
+ {
+ $hwid = Database::insertIgnore('statistic_hw', 'hwid', ['hwtype' => $hwType, 'hwname' => $uniqueName]);
+ $vals = [];
+ foreach ($props as $k => $v) {
+ $vals[] = [$hwid, $k, $v];
+ }
+ if (!empty($vals)) {
+ Database::exec("INSERT IGNORE INTO statistic_hw_prop (hwid, prop, `value`) VALUES :vals",
+ ['vals' => $vals]);
+ }
+ return $hwid;
+ }
+
+ /**
+ * Takes hwinfo json, then looks up and returns all sections from the
+ * dmidecode subtree that represent the given dmi table entry type,
+ * e.g. 17 for memory. It will then return an array of 'props' subtrees.
+ * @param array $data hwinfo tree
+ * @param int $type dmi type
+ * @return array [ <props>, <props>, ... ]
+ */
+ private static function getDmiHandles(array $data, int $type): array
+ {
+ if (empty($data['dmidecode']))
+ return [];
+ $ret = [];
+ foreach ($data['dmidecode'] as $section) {
+ if ($section['handle']['type'] == $type) {
+ $ret[] = $section['props'];
+ }
+ }
+ return $ret;
+ }
+
+ const SIZE_LOOKUP = ['T' => 1099511627776, 'G' => 1073741824, 'M' => 1048576, 'K' => 1024];
+
+ /**
+ * Takes an array of type [ key1 => [ 'values' => [ <val1.1>, <val1.2>, ... ] ], key2 => ... ]
+ * and turns it into [ key1 => <val1.1>, key2 => <val2.1>, ... ]
+ * Along the way, any fields with bogus values, or values analogous to empty will get removed
+ */
+ private static function prepareDmiProperties(array $data): array
+ {
+ $ret = [];
+ foreach ($data as $key => $vals) {
+ $val = trim($vals['values'][0]);
+ if ($val === '[Empty]' || $val === 'NULL')
+ continue;
+ $val = preg_replace('/[^a-z0-9]/', '', strtolower($val));
+ if ($val === '' || $val === 'notspecified' || $val === 'tobefilledbyoem' || $val === 'unknown'
+ || $val === 'chassismanufacture' || $val === 'chassismanufacturer' || $val === 'chassisversion'
+ || $val === 'chassisserialnumber' || $val === 'defaultstring' || $val === 'productname'
+ || $val === 'manufacturer' || $val === 'systemmodel') {
+ continue;
+ }
+ $val = trim($vals['values'][0] ?? '');
+ if ($key === 'Manufacturer') {
+ $val = self::fixManufacturer($val);
+ } elseif (preg_match('/^(\d+)\s+([TGMK])B$/i', $val, $out)) {
+ $val = $out[1] * self::SIZE_LOOKUP[strtoupper($out[2])];
+ }
+ $ret[$key] = $val;
+ }
+ return $ret;
+ }
+
+ private static function fixManufacturer(string $in): string
+ {
+ $in = Parser::decodeJedec($in);
+ switch (strtolower($in)) {
+ case 'advanced micro devices, inc.':
+ case 'advanced micro devices':
+ case 'authenticamd':
+ return 'AMD';
+ case 'apple inc.':
+ return 'Apple';
+ case 'asustek computer inc.':
+ return 'ASUS';
+ case 'dell inc.':
+ return 'Dell';
+ case 'fujitsu':
+ case 'fujitsu client computing limited':
+ return 'Fujitsu';
+ case 'hewlett packard':
+ case 'hewlett-packard':
+ return 'HP';
+ case 'hynix semiconduc':
+ case 'hynix/hyundai':
+ case 'hyundai electronics hynix semiconductor inc':
+ case 'hynix semiconductor inc sk hynix':
+ return 'SK Hynix';
+ case 'genuineintel':
+ case 'intel corporation':
+ case 'intel(r) corp.':
+ case 'intel(r) corporation':
+ return 'Intel';
+ case 'samsung sdi':
+ return 'Samsung';
+ }
+ return $in;
+ }
+
+ private static function writeLocalHardwareData(string $uuid, int $hwid, string $pathId, array $props): int
+ {
+ // Add mapping between hw entity and machine
+ $mappingId = Database::insertIgnore('machine_x_hw', 'machinehwid',
+ ['hwid' => $hwid, 'machineuuid' => $uuid, 'devpath' => $pathId],
+ ['disconnecttime' => 0]);
+ // And all the properties specific to this entity instance (e.g. serial number)
+ if (!empty($props)) {
+ $vals = [];
+ foreach ($props as $k => $v) {
+ $vals[] = [$mappingId, $k, $v];
+ }
+ Database::exec("INSERT IGNORE INTO machine_x_hw_prop (machinehwid, prop, value)
+ VALUES :vals", ['vals' => $vals]);
+ }
+ return $mappingId;
+ }
+
+}
diff --git a/modules-available/statistics/inc/parser.inc.php b/modules-available/statistics/inc/parser.inc.php
index 84f98c40..150639c3 100644
--- a/modules-available/statistics/inc/parser.inc.php
+++ b/modules-available/statistics/inc/parser.inc.php
@@ -120,14 +120,16 @@ class Parser {
* Convert/format size unit. Input string can be a size like
* 8 GB or 1024 MB and will be converted according to passed parameters.
* @param string $string Input string
- * @param string $scale 'a' for auto, T/G/M/K for according units
+ * @param string $scale 'a' for auto, T/G/M/K/'' for according units
* @param bool $appendUnit append unit string, e.g. 'GiB'
* @return string|int Formatted result
*/
- private static function convertSize($string, $scale = 'a', $appendUnit = true)
+ public static function convertSize(string $string, string $scale = 'a', bool $appendUnit = true)
{
- if (!preg_match('/(\d+)\s*([TGMK]?)/i', $string, $out))
+ if (!preg_match('/(\d+)\s*([TGMK]?)/i', $string, $out)) {
+ error_log("Not size: $string");
return false;
+ }
$val = (int)$out[1] * self::LOOKUP[strtoupper($out[2])];
if (!array_key_exists($scale, self::LOOKUP)) {
foreach (self::LOOKUP as $k => $v) {
@@ -385,12 +387,13 @@ class Parser {
}
}
- public static function decodeJedec($string)
+ public static function decodeJedec(string $string): string
{
// JEDEC ID:7F 7F 9E 00 00 00 00 00
if (preg_match('/JEDEC(?:\s*ID)?\s*:\s*([0-9a-f\s]+)/i', $string, $out)) {
preg_match_all('/[0-9a-f]{2}/i', $out[1], $out);
$bank = 0;
+ $id = 0;
foreach ($out[0] as $id) {
$bank++;
$id = hexdec($id) & 0x7f; // Let's just ignore the parity bit, and any potential error
diff --git a/modules-available/statistics/install.inc.php b/modules-available/statistics/install.inc.php
index 15d0d633..99cc8277 100644
--- a/modules-available/statistics/install.inc.php
+++ b/modules-available/statistics/install.inc.php
@@ -64,7 +64,7 @@ $res[] = $machineHwCreate = tableCreate('machine_x_hw', "
`machinehwid` int(10) unsigned NOT NULL AUTO_INCREMENT,
`hwid` int(10) unsigned NOT NULL,
`machineuuid` char(36) CHARACTER SET ascii NOT NULL,
- `devpath` char(50) CHARACTER SET ascii NOT NULL,
+ `devpath` char(32) CHARACTER SET ascii NOT NULL,
`disconnecttime` int(10) unsigned NOT NULL COMMENT 'time the device was not connected to the pc anymore for the first time, 0 if it is connected',
PRIMARY KEY (`machinehwid`),
UNIQUE KEY `hwid` (`hwid`,`machineuuid`,`devpath`),
@@ -74,22 +74,22 @@ $res[] = $machineHwCreate = tableCreate('machine_x_hw', "
$res[] = tableCreate('machine_x_hw_prop', "
`machinehwid` int(10) unsigned NOT NULL,
- `prop` char(16) CHARACTER SET ascii NOT NULL,
+ `prop` varchar(64) CHARACTER SET ascii NOT NULL,
`value` varchar(500) NOT NULL,
PRIMARY KEY (`machinehwid`,`prop`)
");
$res[] = tableCreate('statistic_hw', "
`hwid` int(10) unsigned NOT NULL AUTO_INCREMENT,
- `hwtype` char(11) CHARACTER SET ascii NOT NULL,
- `hwname` varchar(200) NOT NULL,
+ `hwtype` char(16) CHARACTER SET ascii NOT NULL,
+ `hwname` char(32) CHARACTER SET ascii NOT NULL,
PRIMARY KEY (`hwid`),
UNIQUE KEY `hwtype` (`hwtype`,`hwname`)
");
$res[] = tableCreate('statistic_hw_prop', "
`hwid` int(10) unsigned NOT NULL,
- `prop` char(16) CHARACTER SET ascii NOT NULL,
+ `prop` varchar(64) CHARACTER SET ascii NOT NULL,
`value` varchar(500) NOT NULL,
PRIMARY KEY (`hwid`,`prop`)
");
@@ -298,6 +298,35 @@ if (!tableHasColumn('machine', 'live_id45size')) {
}
$res[] = UPDATE_DONE;
}
-
+// 2021-08-19 Enhanced machine property indexing
+if (stripos(tableColumnType('statistic_hw_prop', 'prop'), 'varchar(64)') === false) {
+ $ret = Database::exec("ALTER TABLE statistic_hw_prop MODIFY `prop` varchar(64) CHARACTER SET ascii NOT NULL");
+ if ($ret === false) {
+ finalResponse(UPDATE_FAILED, 'Changing prop of statistic_hw_prop failed: ' . Database::lastError());
+ }
+ $res[] = UPDATE_DONE;
+}
+if (stripos(tableColumnType('machine_x_hw_prop', 'prop'), 'varchar(64)') === false) {
+ $ret = Database::exec("ALTER TABLE machine_x_hw_prop MODIFY `prop` varchar(64) CHARACTER SET ascii NOT NULL");
+ if ($ret === false) {
+ finalResponse(UPDATE_FAILED, 'Changing prop of machine_x_hw_prop failed: ' . Database::lastError());
+ }
+ $res[] = UPDATE_DONE;
+}
+if (stripos(tableColumnType('statistic_hw', 'hwname'), 'char(32)') === false) {
+ $ret = Database::exec("ALTER TABLE statistic_hw MODIFY `hwname` char(32) CHARACTER SET ascii NOT NULL,
+ MODIFY `hwtype` char(16) CHARACTER SET ascii NOT NULL");
+ if ($ret === false) {
+ finalResponse(UPDATE_FAILED, 'Changing hwname/hwtype of statistic_hw failed: ' . Database::lastError());
+ }
+ $res[] = UPDATE_DONE;
+}
+if (stripos(tableColumnType('machine_x_hw', 'devpath'), 'char(32)') === false) {
+ $ret = Database::exec("ALTER TABLE machine_x_hw MODIFY `devpath` char(32) CHARACTER SET ascii NOT NULL");
+ if ($ret === false) {
+ finalResponse(UPDATE_FAILED, 'Changing devpath of machine_x_hw failed: ' . Database::lastError());
+ }
+ $res[] = UPDATE_DONE;
+}
// Create response
responseFromArray($res);