active_mib = $mib; } public function __destruct() { } public function uninstall() : mixed { // avoid that our default mib will be dropped by some plugin developer if ($this->active_mib == 'CACTI-MIB') { return false; } else { db_execute_prepared('DELETE FROM snmpagent_cache WHERE `mib` = ?', [$this->active_mib]); db_execute_prepared('DELETE FROM snmpagent_cache_notifications WHERE `mib` = ?', [$this->active_mib]); db_execute_prepared('DELETE FROM snmpagent_cache_textual_conventions WHERE `mib` = ?', [$this->active_mib]); db_execute_prepared('DELETE FROM snmpagent_mibs WHERE `name` = ?', [$this->active_mib]); return true; } } public function install(string $path, bool $replace = false, string $mib_name = 'optional') : mixed { include_once(__DIR__ . '/mib_parser.php'); $mp = new MibParser(); $mp->add_mib($path, $mib_name); $mp->generate(); if (isset($mp->mib) && $mp->mib) { // check if this mib has already been installed $existing = db_fetch_cell_prepared('SELECT 1 FROM snmpagent_mibs WHERE `name` = ?', [$mp->mib]); if ($existing) { if ($replace == false) { unset($mp->oids); unset($mp->mib); return false; } else { $this->uninstall(); } } db_execute_prepared('INSERT INTO snmpagent_mibs SET `id` = 0, `name` = ?, `file` = ?', [$mp->mib, $path]); foreach ($mp->oids as $object_name => $object_params) { if ($object_params['otype'] != 'TEXTUAL-CONVENTION') { db_execute_prepared('INSERT IGNORE INTO `snmpagent_cache` (`oid`, `name`, `mib`, `type`, `otype`, `kind`, `max-access`, `description`) VALUES (?, ?, ?, ?, ?, ?, ?, ?)', [$object_params['oid'], $object_name, $object_params['mib'], $object_params['syntax'], $object_params['otype'], $object_params['kind'], $object_params['max-access'], str_replace("\r\n", '
', trim($object_params['description']))]); if ($object_params['otype'] == 'NOTIFICATION-TYPE') { foreach ($object_params['objects'] as $notification_object_index => $notification_object) { db_execute_prepared('INSERT INTO `snmpagent_cache_notifications` (`name`, `mib`, `attribute`, `sequence_id`) VALUES (?, ?, ?, ?) ON DUPLICATE KEY UPDATE `sequence_id` = VALUES(`sequence_id`)', [$object_name, $object_params['mib'], $notification_object, $notification_object_index]); } } } else { db_execute_prepared('INSERT INTO `snmpagent_cache_textual_conventions` (`name`, `mib`, `type`, `description`) VALUES (?, ?, ?, ?) ON DUPLICATE KEY UPDATE `description` = VALUES(`description`)', [$object_name, $object_params['mib'], $object_params['syntax'], nl2br($object_params['description'])]); } } unset($mp->oids); unset($mp->mib); return true; } else { return false; } } public function mib(string $mib) : self { $this->active_mib = $mib; $this->active_object = ''; $this->active_table = ''; $this->active_table_entry = ''; return $this; } public function object(string $object) : self { $this->active_object = $object; return $this; } public function table(string $table) : self { if ($this->active_table != $table) { if (!isset($this->cache__tables[$this->active_mib][$table])) { $oid_table = db_fetch_cell_prepared('SELECT oid FROM `snmpagent_cache` WHERE `mib` = ? AND `name` = ? AND `type` = "SEQUENCE OF"', [$this->active_mib, $table]); if ($oid_table) { // cache table oid and columns $this->cache__tables[$this->active_mib][$table] = $oid_table; $this->active_table = $table; $this->cache__tables_columns[$this->active_mib][$table] = $this->columns(); $this->active_table_entry = ''; return $this; } else { // MIB table does not exist $this->active_table = ''; $this->active_table_entry = ''; throw new Exception('MIB table does not exist'); } } else { // table exists and has already been cached $this->active_table = $table; $this->active_table_entry = ''; return $this; } } else { // no changes necessary return $this; } } public function row(mixed $index) : self { // limited to one single $index so far $this->active_table_entry = $index; return $this; } public function gettype() : void { } public function set(mixed $value) : mixed { return db_execute_prepared('UPDATE `snmpagent_cache` SET `value` = ? WHERE `mib` = ? AND `name` = ?', [$value, $this->active_mib, $this->active_object]); } public function get() : mixed { return db_fetch_row_prepared('SELECT * FROM snmpagent_cache WHERE name = ? AND mib = ?', [$this->active_object, $this->active_mib]); } public function count() : mixed { return db_execute_prepared('UPDATE snmpagent_cache SET `value` = CASE WHEN `type`="Counter32" AND `value`= 4294967295 THEN 0 WHEN `type`="Counter64" AND `value`= 18446744073709551615 THEN 0 ELSE `value`+1 END WHERE `mib` = ? AND `name` = ?', [$this->active_mib, $this->active_object]); } public function insert(array $values) : bool { $oid_entry = $this->exists(); if ($oid_entry == false) { $columns = $this->cache__tables_columns[$this->active_mib][$this->active_table]; if ($columns && cacti_sizeof($columns) > 0) { foreach ($columns as $column_params) { $column_params['oid'] .= '.' . $this->active_table_entry; $column_params['otype'] = 'DATA'; if (isset($values[$column_params['name']])) { $column_params['value'] = $values[$column_params['name']]; } db_execute_prepared('INSERT INTO `snmpagent_cache` (`oid`, `name`, `mib`, `type`, `otype`, `kind`, `max-access`, `value`) VALUES (?, ?, ?, ?, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE `name`=VALUES(`name`), `mib`=VALUES(`mib`), `type`=VALUES(`type`), `otype`=VALUES(`otype`), `kind`=VALUES(`kind`), `max-access`=VALUES(`max-access`), `value`=VALUES(`value`)', [$column_params['oid'], $column_params['name'], $column_params['mib'], $column_params['type'], $column_params['otype'], 'Column Data', $column_params['max-access'], trim($column_params['value'])]); } return true; } } return false; } public function select(mixed $column = false) : mixed { $result = []; if ($this->active_table_entry) { // focus on a dedicated MIB table row only $oid_entry = $this->exists(); if ($oid_entry !== false) { if ($column == false) { // fetch the whole row $filter = $oid_entry . '.%.' . $this->active_table_entry; $entries = db_fetch_assoc_prepared('SELECT name, value FROM snmpagent_cache WHERE oid LIKE ? GROUP BY name ORDER BY oid', [$filter]); if ($entries && cacti_sizeof($entries) > 0) { foreach ($entries as $entry) { $result[$entry['name']] = $entry['value']; } return $result; } } elseif (is_string($column)) { // fetch only the value of a given column $filter = $oid_entry . '.%.' . $this->active_table_entry; return db_fetch_cell_prepared('SELECT value FROM snmpagent_cache WHERE name = ? AND oid LIKE ? LIMIT 1', [$column, $filter]); } elseif (is_array($column) && cacti_sizeof($column) > 0) { $filter = $oid_entry . '.%.' . $this->active_table_entry; // fetch all values of specific columns given for that MIB table row /* The names are bound rather than pasted between quotes. The caller passes them, and a quote in one used to close the string and leave the rest as SQL. */ $placeholders = implode(',', array_fill(0, cacti_sizeof($column), '?')); $entries = db_fetch_assoc_prepared("SELECT name, value FROM snmpagent_cache WHERE name IN ($placeholders) AND oid LIKE ? GROUP BY name ORDER BY oid", array_merge(array_values($column), [$filter])); if ($entries && cacti_sizeof($entries) > 0) { foreach ($entries as $entry) { $result[$entry['name']] = $entry['value']; } return $result; } } } } else { // query the whole MIB table $oid_entry = $this->cache__tables[$this->active_mib][$this->active_table] . '.1'; if ($column == false) { // fetch all rows $columns = $this->cache__tables_columns[$this->active_mib][$this->active_table]; $num_columns = cacti_sizeof($columns); $filter = $oid_entry . '.%.%'; $entries = db_fetch_assoc_prepared('SELECT name, value FROM snmpagent_cache WHERE oid LIKE ? ORDER BY oid', [$filter]); if ($num_columns && $entries && cacti_sizeof($entries)) { $num_entries = cacti_sizeof($entries); $entries_per_object = $num_entries / $num_columns; for ($i = 0; $i < $entries_per_object; $i++) { $result[$i] = []; for ($j = 0; $j < $num_columns; $j++) { $index = (int) ($i + $j * $entries_per_object); $result[$i][$entries[$index]['name']] = $entries[$index]['value']; } } return $result; } else { return $entries; } } elseif (is_string($column)) { // fetch only the values of one single column $filter = $oid_entry . '.%.%'; /* The alias is an identifier and cannot be bound, so confine it to the characters one may hold. It was pasted between quotes before, where a quote in the name ended the alias. */ /* The helper's default is 'id', which would quietly rename the returned key to a real column name when the input strips to nothing. Ask for an empty default and refuse instead. */ $alias = sanitize_sql_column($column, ''); if ($alias === '') { return []; } return db_fetch_assoc_prepared('SELECT value AS `' . $alias . '` FROM snmpagent_cache WHERE name = ? AND oid LIKE ? ORDER BY oid', [$column, $filter]); } elseif (is_array($column) && cacti_sizeof($column) > 0) { // fetch values of specific columns given $filter = $oid_entry . '.%.%'; $placeholders = implode(',', array_fill(0, cacti_sizeof($column), '?')); $entries = db_fetch_assoc_prepared("SELECT name, value FROM snmpagent_cache WHERE name IN ($placeholders) AND oid LIKE ? ORDER BY oid", array_merge(array_values($column), [$filter])); if (cacti_sizeof($entries)) { $num_objects = cacti_sizeof($column); $num_entries = cacti_sizeof($entries); $entries_per_object = (int) ceil($num_entries / $num_objects); for ($i = 0; $i < $entries_per_object; $i++) { $result[$i] = []; for ($j = 0; $j < $num_objects; $j++) { $index = (int) ($i + ($j * $entries_per_object)); $result[$i][$entries[$index]['name']] = $entries[$index]['value']; } } return $result; } else { return $entries; } } } return false; } public function delete() : bool { $oid_entry = $this->exists(); if ($oid_entry !== false) { // get list of columns for this mib table $columns = $this->cache__tables_columns[$this->active_mib][$this->active_table]; if ($columns && cacti_sizeof($columns) > 0) { foreach ($columns as $column_params) { $column_params['oid'] .= '.' . $this->active_table_entry; db_execute_prepared('DELETE FROM `snmpagent_cache` WHERE `oid` = ?', [$column_params['oid']]); } return true; } } return false; } public function update(array $values) : bool { $oid_entry = $this->exists(); if ($oid_entry !== false) { $columns = $this->cache__tables_columns[$this->active_mib][$this->active_table]; if (cacti_sizeof($columns) > 0) { $sql = []; foreach ($columns as $column_params) { $column_params['oid'] .= '.' . $this->active_table_entry; if (isset($values[$column_params['name']])) { $sql[] = '(' . db_qstr($column_params['name']) . ', ' . db_qstr($values[$column_params['name']]) . ', ' . db_qstr($column_params['oid']) . ')'; } } if (cacti_sizeof($sql)) { db_execute('INSERT INTO `snmpagent_cache` (name, value, oid) VALUES ' . implode(', ', $sql) . ' ON DUPLICATE KEY UPDATE value=VALUES(value)'); } return true; } } return false; } public function replace(array $values) : bool { $this->delete(); return $this->insert($values); } public function truncate() : bool { $oid_entry = $this->cache__tables[$this->active_mib][$this->active_table] . '.1.%'; db_execute_prepared('DELETE FROM `snmpagent_cache` WHERE `mib` = ? AND `otype` = "DATA" AND `oid` LIKE ?', [$this->active_mib, $oid_entry]); return true; } public function columns() : mixed { /* As defined by SMI the OID value assigned to the row must be the same as the OID value assigned to the table containing the row with addition of a single value of one. */ $filter = $this->cache__tables[$this->active_mib][$this->active_table] . '.1.%'; return db_fetch_assoc_prepared('SELECT * FROM `snmpagent_cache` WHERE `oid` LIKE ? GROUP BY name ORDER BY oid', [$filter]); } private function exists() : bool { $oid_entry = $this->cache__tables[$this->active_mib][$this->active_table] . '.1'; // check if entry exists $exists = db_fetch_cell_prepared('SELECT 1 FROM `snmpagent_cache` WHERE `oid` = ?', [$oid_entry . '.1.' . $this->active_table_entry]); return ($exists) ? $oid_entry : false; } }