323 lines
13 KiB
PHP

<?php
/**
* Observium
*
* This file is part of Observium.
*
* @package observium
* @subpackage db
* @copyright (C) Adam Armstrong
*
*/
if (!defined('OBS_DEBUG')) {
// Direct call isn't allowed.
echo("WARNING. Direct call to this script is no longer supported, please use './discovery.php -u' from main observium directory.\n");
exit(2);
}
// One time alert about deprecated (eol) mysql version
get_versions();
if ($GLOBALS['cache']['versions']['mysql_old']) {
$mysql_name = $GLOBALS['cache']['versions']['mysql_name'];
$mysql_version = $GLOBALS['cache']['versions']['mysql_version'];
print_message("
+---------------------------------------------------------+
| |
| %rDANGER! ACHTUNG! BHUMAHUE!%n |
| |
" .
str_pad("| %WYour " . $mysql_name . " version is too old (%r" . $mysql_version . "%W),", 64, ' ') . "%n|
| %Wfunctionality may be broken. Please update your " . $mysql_name . "!%n |
| |
| See additional information here: |
| %c" .
str_pad(OBSERVIUM_DOCS_URL . '/software_requirements/', 56, ' ') . "%n|
| |
+---------------------------------------------------------+
", 'color');
}
/**
* Tests with initial db schema install (252):
*
* 5.5.46, no strict:
* sql_mode
* series 2min 14.535s
* install 1min 4.387s
*
* 5.6.27, no strict:
* sql_mode NO_ENGINE_SUBSTITUTION
* series 8min 14.076s
* install 4min 2.166s
*
* 5.7.9, strict:
* sql_mode ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION
* series 5min 47.310s
* install 3min 14.853s
*/
$db_rev = get_db_version();
// DB schema not installed
$schema_insert = ($db_rev == 0 && !dbQuery('SELECT 1 FROM `devices` LIMIT 1;'));
// Try to use mysql cmd for insert initial db schema
if ($schema_insert && is_file($config['install_dir'] . '/update/db_schema_mysql.sql')) {
if (is_executable('/usr/bin/mysql')) {
// Default path
$mysql_cmd = '/usr/bin/mysql';
} else {
// Try to find mysql executable in search paths
$mysql_cmd = external_exec('which mysql');
}
if (is_executable($mysql_cmd)) {
// If mysql executable exist (or find) use insert initial schema
$cmd = $mysql_cmd .
' -u' . escapeshellarg($config['db_user']) .
' -p' . escapeshellarg($config['db_pass']) .
' -h' . escapeshellarg($config['db_host']) .
' -D ' . escapeshellarg($config['db_name']) .
' < ' . escapeshellarg($config['install_dir'] . '/update/db_schema_mysql.sql');
echo('Install initial database schema ...');
external_exec($cmd, $exec_status);
$mysql_status = $exec_status['exitcode'] === 0;
// Recheck if initial schema installed
$db_rev = get_db_version(TRUE);
$schema_insert = ($db_rev == 0 && !dbQuery('SELECT 1 FROM `devices` LIMIT 1;'));
if ($mysql_status) {
echo(' done.' . PHP_EOL);
} else {
echo(' FALSE.' . PHP_EOL);
if (!$schema_insert) {
print_error("Error during installation initial schema, but tables exist. Run update again."); // Not should happen NEVER
exit(2);
}
}
}
}
if ($db_rev > 272) { // observium_processes added in db version 272
// Check if discovery -u already running
$pid_info = check_process_run(-1);
if ($pid_info) {
// Process ID exist in DB
print_message("%rAnother " . $pid_info['process_name'] . " process (PID: " . $pid_info['PID'] . ", UID: " . $pid_info['UID'] . ", STARTED: " . $pid_info['STARTED'] . ") already running for update.%n", 'color');
// Not sure what better, return and process all other discovery operations
// or complete stop discovery
// (stop can produce 6 hour latency for devices discovery, but return can broke all discovery devices after update)
return FALSE;
//exit(2);
}
add_process_info(-1); // Store process info
}
// Note, undocumented ability for force update from db schema (not more than 50)
$update_force = isset($options['U']) && is_numeric($options['U']) &&
$db_rev >= $options['U'] && ($db_rev - $options['U']) <= 50;
if ($update_force) {
print_debug("Forced update from DB schema " . $options['U']);
$db_rev = (int)$options['U'] - 1;
}
$updating = 0;
// Only numeric filenames (001.sql, 013.php)
$sql_regexp = "/^\d{3,4}\.sql$/";
$php_regexp = "/^\d{3,4}\.php$/";
$filelist = [];
if ($handle = opendir($config['install_dir'] . '/update')) {
while (FALSE !== ($file = readdir($handle))) {
if (filetype($config['install_dir'] . '/update/' . $file) === 'file' && (preg_match($sql_regexp, $file) || preg_match($php_regexp, $file))) {
$filelist[] = $file;
}
}
closedir($handle);
}
sort($filelist);
//print_vars($filelist);
foreach ($filelist as $file) {
$filepath = $config['install_dir'] . '/update/' . $file;
[$filename, $extension] = explode('.', $file, 2);
if ($filename > $db_rev) {
if (!$updating) {
echo('-- Updating database/file schema' . PHP_EOL);
}
$error_ignore = $update_force; // Stop update if errors not ignored
if ($extension === "php") {
$log_msg = sprintf("%03d", $db_rev) . " -> " . sprintf("%03d", $filename) . " # (php) ";
echo($log_msg);
$start = time();
if (include_wrapper($filepath)) {
// File included OK, update dbSchema
$schema_status = set_db_version($filename, $schema_insert);
if ($schema_insert && $schema_status !== FALSE) {
// dbSchema inserted, now only update
$schema_insert = FALSE;
}
$update_time = format_uptime(time() - $start);
echo(" Done ($update_time)." . PHP_EOL);
if ($filename >= 184) {
log_event("Observium schema updated: " . $log_msg . "($update_time).", NULL, NULL, NULL, 5);
}
} else {
// Critical errors, stop update
logfile('update-errors.log', "====== Schema update " . sprintf("%03d", $db_rev) . " -> " . sprintf("%03d", $filename) . " ==============");
logfile('update-errors.log', "Error: Could not load file $filepath!");
if ($filename >= 184) {
log_event("Observium schema not updated: " . $log_msg . ".", NULL, NULL, NULL, 3);
}
exit(1);
}
} elseif ($extension === "sql") {
$log_msg = sprintf("%03d", $db_rev) . " -> " . sprintf("%03d", $filename) . " # (db) ";
echo($log_msg);
$err = 0;
$start = time();
if ($fd = @fopen($filepath, 'r')) {
$data = fread($fd, 4096);
while (!feof($fd)) {
$data .= fread($fd, 4096);
}
fclose($fd);
foreach (explode("\n", $data) as $line) {
if (trim($line)) {
// Skip comments
if (str_starts($line, ['#', '-', '/'])) {
if (str_contains_array($line, ['ERROR_IGNORE', 'IGNORE_ERROR'])) {
$error_ignore = TRUE;
} elseif (str_contains($line, 'NOTE')) {
[, $note] = explode('NOTE', $line, 2);
echo('(' . trim($note) . ')');
}
continue;
}
print_debug($line);
$update = dbQuery($line);
if (!$update) {
$error_no = dbErrorNo();
$error_msg = "($error_no) " . dbError();
if ($error_no >= 2000 || in_array($error_no, [3, 1114])) { // additional critical errors list
// Critical errors, stop update
log_event("Observium schema not updated: " . $log_msg . ".", NULL, NULL, NULL, 3);
echo(" stopped. Critical error: " . $error_msg . PHP_EOL);
// http://dev.mysql.com/doc/refman/5.6/en/error-messages-client.html
logfile('update-errors.log', "====== Schema update " . sprintf("%03d", $db_rev) . " -> " . sprintf("%03d", $filename) . " ==============");
logfile('update-errors.log', "Query: " . $line);
logfile('update-errors.log', "Error: " . $error_msg);
del_process_info(-1); // Remove process info
exit(1);
}
if ($error_ignore) {
echo('.');
} else {
echo('F');
}
$err++;
$errors[] = ['query' => $line, 'error' => $error_msg];
print_debug($error_msg);
} else {
echo('.');
}
}
}
$update_time = format_uptime(time() - $start);
if ($db_rev < 1) {
if ($filename >= 184) {
log_event("Observium schema updated: " . $log_msg . "($update_time).", NULL, NULL, NULL, 5);
}
echo(" Done ($update_time)." . PHP_EOL);
} elseif ($err) {
if ($error_ignore) {
if ($filename >= 184) {
log_event("Observium schema updated: " . $log_msg . "($update_time).", NULL, NULL, NULL, 5);
}
echo(" Done ($update_time)." . PHP_EOL);
} else {
if ($filename >= 184) {
log_event("Observium schema updated: " . $log_msg . "($update_time, $err errors).", NULL, NULL, NULL, 4);
}
echo(" Done ($update_time, $err errors)." . PHP_EOL);
}
logfile('update-errors.log', "====== Schema update " . sprintf("%03d", $db_rev) . " -> " . sprintf("%03d", $filename) . " ==============");
foreach ($errors as $error) {
logfile('update-errors.log', "Query: " . $error['query']);
logfile('update-errors.log', "Error: " . $error['error']);
}
unset($errors);
} else {
if ($filename >= 184) {
log_event("Observium schema updated: " . $log_msg . "($update_time).", NULL, NULL, NULL, 5);
}
echo(" Done ($update_time)." . PHP_EOL);
}
// SQL update done, update dbSchema
$schema_status = set_db_version($filename, $schema_insert);
if ($schema_insert && $schema_status !== FALSE) {
// dbSchema inserted, now only update
$schema_insert = FALSE;
}
/// Only for developers, export latest schema
if ($schema_status && $filename >= 300 && OBS_DEBUG > 1 &&
!is_file($config['install_dir'] . '/update/db_schema_' . $filename . '.json')) {
file_put_contents($config['install_dir'] . '/update/db_schema_' . $filename . '.json', export_db_schema('json'));
}
} else {
if ($filename >= 184) {
log_event("Observium schema not updated: " . $log_msg . ".", NULL, NULL, NULL, 3);
}
echo(' Could not open file!' . PHP_EOL);
// Critical errors, stop update
logfile('update-errors.log', "====== Schema update " . sprintf("%03d", $db_rev) . " -> " . sprintf("%03d", $filename) . " ==============");
logfile('update-errors.log', "Error: Could not open file $filepath!");
del_process_info(-1); // Remove process info
exit(1);
}
}
$updating++;
$db_rev = $filename;
}
}
// Clean
del_process_info(-1); // Remove process info
if ($updating) {
// $GLOBALS['cache']['db_version'] = $db_rev; // Cache new db version
// if ($schema_insert)
// {
// dbInsert(array('attrib_type' => 'dbSchema', 'attrib_value' => $db_rev), 'observium_attribs');
// } else {
// dbUpdate(array('attrib_value' => $db_rev), 'observium_attribs', 'attrib_type = ?', array('dbSchema'));
// }
echo('-- Done.' . PHP_EOL);
} else {
echo('-- Database is up to date.' . PHP_EOL);
}
return $updating;
// EOF