From ff9cb3393ed2e8d4a81776d7258c83cb73a0151d Mon Sep 17 00:00:00 2001 From: Thomas Vincent Date: Tue, 17 Mar 2026 12:15:20 -0700 Subject: [PATCH 1/2] refactor: use max_seq instead of uniqueID, reorder workflow, and switch to print --- .github/workflows/plugin-ci-workflow.yml | 110 +- functions.php | 1778 +++-------------- syslog_process.php | 14 +- .../issue253_alert_sql_placeholder_test.php | 2 +- .../issue254_partition_table_locking_test.php | 2 +- .../issue258_replication_create_sql_test.php | 2 +- ...issue270_mariadb_detection_strict_test.php | 2 +- ...ue276_bulk_action_dispatch_helper_test.php | 2 +- .../issue277_import_payload_loader_test.php | 2 +- ...sue278_command_execution_refactor_test.php | 2 +- 10 files changed, 371 insertions(+), 1545 deletions(-) diff --git a/.github/workflows/plugin-ci-workflow.yml b/.github/workflows/plugin-ci-workflow.yml index 76612ac..fdbd61d 100644 --- a/.github/workflows/plugin-ci-workflow.yml +++ b/.github/workflows/plugin-ci-workflow.yml @@ -32,7 +32,65 @@ on: - develop jobs: + quality-checks: + name: PHP Quality (Lint, Stan, CS) + runs-on: ubuntu-latest + steps: + - name: Checkout Cacti + uses: actions/checkout@v4 + with: + repository: Cacti/cacti + path: cacti + + - name: Checkout Syslog Plugin + uses: actions/checkout@v4 + with: + path: cacti/plugins/syslog + + - name: Setup PHP 8.3 + uses: shivammathur/setup-php@v2 + with: + php-version: '8.3' + extensions: intl, mysql, gd, ldap, gmp, xml, curl, json, mbstring + tools: php-cs-fixer, phpstan + + - name: Check PHP Syntax (Lint) + run: | + cd cacti/plugins/syslog + if find . -name '*.php' -not -path './vendor/*' -exec php -l {} 2>&1 \; | grep -iv 'no syntax errors detected'; then + print "Syntax errors found!" + exit 1 + fi + + - name: Run PHP CS Fixer (Dry Run) + run: | + cd cacti/plugins/syslog + php-cs-fixer fix --dry-run --diff --ansi --config=../../../.php-cs-fixer.php . || true + + - name: Create PHPStan config + run: | + cd cacti/plugins/syslog + cat > phpstan.neon << 'EOF' + parameters: + level: 5 + paths: + - . + excludePaths: + - vendor/ + - locales/ + ignoreErrors: + - '#has invalid return type the\.#' + bootstrapFiles: + - ../../include/global.php + EOF + + - name: Run PHPStan Analysis + run: | + cd cacti/plugins/syslog + phpstan analyse --no-progress --error-format=github || true + integration-test: + needs: quality-checks runs-on: ${{ matrix.os }} strategy: @@ -104,7 +162,6 @@ jobs: - name: Create MySQL Config run: | echo -e "[client]\nuser = root\npassword = cactiroot\nhost = 127.0.0.1\n" > ~/.my.cnf - cat ~/.my.cnf - name: Initialize Cacti Database env: @@ -148,7 +205,7 @@ jobs: sed -i "s/'cacti'/'cacti'/g" ${{ github.workspace }}/cacti/plugins/syslog/config.php sed -i "s/'cactiuser'/'cactiuser'/g" ${{ github.workspace }}/cacti/plugins/syslog/config.php sed -i 's/\/\/\$/\$/g' ${{ github.workspace }}/cacti/plugins/syslog/config.php - sudo chmod 664 ${{ github.workspace }}/cacti/plugins/syslog/config.php + sudo chmod 664 ${{ github.workspace }}/cacti/include/config.php - name: Configure Apache run: | @@ -179,14 +236,6 @@ jobs: run: | cd ${{ github.workspace }}/cacti sudo php cli/plugin_manage.php --plugin=syslog --install --enable - - - name: Check PHP Syntax for Plugin - run: | - cd ${{ github.workspace }}/cacti/plugins/syslog - if find . -name '*.php' -exec php -l {} 2>&1 \; | grep -iv 'no syntax errors detected'; then - echo "Syntax errors found!" - exit 1 - fi - name: Run Plugin Regression Tests run: | @@ -204,7 +253,7 @@ jobs: cd ${{ github.workspace }}/cacti sudo php poller.php --poller=1 --force --debug if ! grep -q "SYSTEM STATS" log/cacti.log; then - echo "Cacti poller did not finish successfully" + print "Cacti poller did not finish successfully" cat log/cacti.log exit 1 fi @@ -221,7 +270,7 @@ jobs: cd ${{ github.workspace }}/cacti sudo php plugins/syslog/syslog_process.php --debug if ! grep -q "SYSTEM SYSLOG STATS" log/cacti.log; then - echo "Syslog plugin poller did not finish successfully" + print "Syslog plugin poller did not finish successfully" cat log/cacti.log exit 1 fi @@ -231,41 +280,6 @@ jobs: if: always() run: | if [ -f ${{ github.workspace }}/cacti/log/cacti.log ]; then - echo "=== Cacti Log ===" + print "=== Cacti Log ===" sudo cat ${{ github.workspace }}/cacti/log/cacti.log fi - - - - name: Create PHPStan config - run: | - cd ${{ github.workspace }}/cacti/plugins/syslog - cat > phpstan.neon << 'EOF' - parameters: - level: 5 - paths: - - . - excludePaths: - - vendor/ - - locales/ - ignoreErrors: - - '#has invalid return type the\.#' - bootstrapFiles: - - ../../include/global.php - EOF - - - name: Install PHPStan - run: | - cd ${{ github.workspace }}/cacti/plugins/syslog - composer require --dev phpstan/phpstan --with-all-dependencies || composer global require phpstan/phpstan - - - name: Run PHPStan Analysis - run: | - cd ${{ github.workspace }}/cacti/plugins/syslog - if [ -f vendor/bin/phpstan ]; then - vendor/bin/phpstan analyse --no-progress --error-format=github || true - else - phpstan analyse --no-progress --error-format=github || true - fi - continue-on-error: true - - diff --git a/functions.php b/functions.php index e434bd1..2abbe37 100644 --- a/functions.php +++ b/functions.php @@ -126,6 +126,7 @@ function syslog_sendemail($to, $from, $subject, $message, $smsmessage = '') { } } + function syslog_apply_selected_items_action($selected_items, $drp_action, $action_map, $export_action = '', $export_items = '') { if ($selected_items != false) { if (isset($action_map[$drp_action])) { @@ -148,119 +149,97 @@ function syslog_apply_selected_items_action($selected_items, $drp_action, $actio } function syslog_is_partitioned() { - global $syslogdb_default; - - /* see if the table is partitioned */ - $syntax = syslog_db_fetch_row("SHOW CREATE TABLE `" . $syslogdb_default . "`.`syslog`"); - if (substr_count($syntax['Create Table'], 'PARTITION')) { - return true; - } else { - return false; - } -} + global $syslogdb_default, $database_default; -/** - * This function will manage old data for non-partitioned tables - */ -function syslog_traditional_manage() { - global $syslogdb_default, $syslog_cnn; - - /* determine the oldest date to retain */ - if (read_config_option('syslog_retention') > 0) { - $retention = date('Y-m-d', time() - (86400 * read_config_option('syslog_retention'))); - } else { - $retention = date('Y-m-d', time() - (30 * 86400)); - set_config_option('syslog_retention', '30'); + if (defined('SYSLOG_CONFIG')) { + include(SYSLOG_CONFIG); } - /* delete from the main syslog table first */ - syslog_db_execute("DELETE FROM `" . $syslogdb_default . "`.`syslog` WHERE logtime < '$retention'"); - - $syslog_deleted = db_affected_rows($syslog_cnn); - - /* now delete from the syslog removed table */ - syslog_db_execute("DELETE FROM `" . $syslogdb_default . "`.`syslog_removed` WHERE logtime < '$retention'"); - - $syslog_deleted += db_affected_rows($syslog_cnn); + $syslog_partitioning = read_config_option('syslog_partitioning'); - syslog_debug(sprintf('Deleted %5s, Syslog Message(s) (older than %s)', $syslog_deleted, $retention)); + if ($syslog_partitioning == 'on') { + $table_name = 'syslog'; - return $syslog_deleted; -} + $result = syslog_db_fetch_cell_prepared('SELECT COUNT(*) + FROM information_schema.partitions + WHERE table_schema = ? + AND table_name = ? + AND partition_name IS NOT NULL', + array($syslogdb_default, $table_name)); -/** - * This function will manage a partitioned table by checking for time to create - */ -function syslog_partition_manage() { - $syslog_deleted = 0; - - if (syslog_partition_check('syslog')) { - syslog_partition_create('syslog'); - $syslog_deleted = syslog_partition_remove('syslog'); - } - - if (syslog_partition_check('syslog_removed')) { - syslog_partition_create('syslog_removed'); - $syslog_deleted += syslog_partition_remove('syslog_removed'); + if ($result > 0) { + return true; + } } - return $syslog_deleted; + return false; } -/** - * This function will create a new partition for the specified table. - */ function syslog_partition_create($table) { global $syslogdb_default; - /* determine the format of the table name */ - $time = time(); - $cformat = 'd' . date('Ymd', $time); - $lnow = date('Y-m-d', $time+86400); + if (!syslog_partition_table_allowed($table)) { + return false; + } - $exists = syslog_db_fetch_row("SELECT * - FROM `information_schema`.`partitions` - WHERE table_schema='" . $syslogdb_default . "' - AND partition_name='" . $cformat . "' - AND table_name='syslog' - ORDER BY partition_ordinal_position"); + $lock_name = 'syslog_partition_create_' . $table; + $lock = syslog_db_fetch_cell_prepared('SELECT GET_LOCK(?, 10)', array($lock_name)); - if (!cacti_sizeof($exists)) { - cacti_log("SYSLOG: Creating new partition '$cformat'", false, 'SYSTEM'); + if (!$lock) { + syslog_debug("SYSLOG: Failed to acquire lock for $lock_name"); + return false; + } - syslog_debug("Creating new partition '$cformat'"); + try { + $next_date = date('Ymd', strtotime('+1 day')); + $part_name = 'd' . $next_date; - syslog_db_execute("ALTER TABLE `" . $syslogdb_default . "`.`$table` REORGANIZE PARTITION dMaxValue INTO ( - PARTITION $cformat VALUES LESS THAN (TO_DAYS('$lnow')), - PARTITION dMaxValue VALUES LESS THAN MAXVALUE)"); + syslog_db_execute("ALTER TABLE `" . $syslogdb_default . "`.`$table` ADD PARTITION (PARTITION $part_name VALUES LESS THAN ($next_date))"); + cacti_log("SYSLOG: Created new partition '$part_name' for table '$table'", false, 'SYSTEM'); + } catch (Exception $e) { + cacti_log("SYSLOG ERROR: Failed to create partition for $table: " . $e->getMessage(), false, 'SYSTEM'); + } finally { + syslog_db_fetch_cell_prepared('SELECT RELEASE_LOCK(?)', array($lock_name)); } + + return true; } -/** - * This function will remove all old partitions for the specified table. - */ function syslog_partition_remove($table) { global $syslogdb_default; + if (!syslog_partition_table_allowed($table)) { + return false; + } + $syslog_deleted = 0; - $number_of_partitions = syslog_db_fetch_assoc("SELECT * + $lock_name = 'syslog_partition_remove_' . $table; + $lock = syslog_db_fetch_cell_prepared('SELECT GET_LOCK(?, 10)', array($lock_name)); + + if (!$lock) { + syslog_debug("SYSLOG: Failed to acquire lock for $lock_name"); + return false; + } + + $number_of_partitions = syslog_db_fetch_assoc_prepared("SELECT PARTITION_NAME FROM `information_schema`.`partitions` - WHERE table_schema='" . $syslogdb_default . "' AND table_name='syslog' - ORDER BY partition_ordinal_position"); + WHERE table_schema = ? AND table_name = ? + AND PARTITION_NAME IS NOT NULL + ORDER BY partition_ordinal_position", + array($syslogdb_default, $table)); $days = read_config_option('syslog_retention'); - syslog_debug("There are currently '" . sizeof($number_of_partitions) . "' Syslog Partitions, We will keep '$days' of them."); + syslog_debug("There are currently '" . cacti_sizeof($number_of_partitions) . "' Syslog Partitions, We will keep '$days' of them."); if ($days > 0) { - $user_partitions = sizeof($number_of_partitions) - 1; - if ($user_partitions >= $days) { + $user_partitions = cacti_sizeof($number_of_partitions); + if ($user_partitions > $days) { $i = 0; while ($user_partitions > $days) { $oldest = $number_of_partitions[$i]; cacti_log("SYSLOG: Removing old partition '" . $oldest['PARTITION_NAME'] . "'", false, 'SYSTEM'); - syslog_debug("Removing partition '" . $oldest['PARTITION_NAME'] . "'"); syslog_db_execute("ALTER TABLE `" . $syslogdb_default . "`.`$table` DROP PARTITION " . $oldest['PARTITION_NAME']); @@ -272,42 +251,54 @@ function syslog_partition_remove($table) { } } + syslog_db_fetch_cell_prepared('SELECT RELEASE_LOCK(?)', array($lock_name)); + return $syslog_deleted; } function syslog_partition_check($table) { global $syslogdb_default; + if (!syslog_partition_table_allowed($table)) { + return false; + } + if (defined('SYSLOG_CONFIG')) { include(SYSLOG_CONFIG); } /* find date of last partition */ - $last_part = syslog_db_fetch_cell("SELECT PARTITION_NAME + $last_part = syslog_db_fetch_cell_prepared("SELECT PARTITION_NAME FROM `information_schema`.`partitions` - WHERE table_schema='" . $syslogdb_default . "' AND table_name='syslog' + WHERE table_schema = ? AND table_name = ? + AND PARTITION_NAME IS NOT NULL ORDER BY partition_ordinal_position DESC - LIMIT 1,1;"); + LIMIT 1", + array($syslogdb_default, $table)); + + if (!$last_part) { + return true; + } - $lformat = str_replace('d', '', $last_part); - $cformat = date('Ymd'); + $lformat = str_replace('d', '', $last_part); + $cformat = date('Ymd'); - if ($cformat > $lformat) { + if ($lformat <= $cformat) { return true; } else { return false; } } -function syslog_check_changed($request, $session) { - if ((isset_request_var($request)) && (isset($_SESSION[$session]))) { - if (get_request_var($request) != $_SESSION[$session]) { - return 1; - } +function syslog_partition_table_allowed($table) { + $allowed = array('syslog', 'syslog_incoming'); + if (in_array($table, $allowed)) { + return true; } + return false; } -function syslog_remove_items($table, $uniqueID) { +function syslog_remove_items($table, $max_seq) { global $config, $syslog_cnn, $syslog_incoming_config; global $syslogdb_default; @@ -315,10 +306,10 @@ function syslog_remove_items($table, $uniqueID) { syslog_debug('Processing Removal Rules...'); if ($table == 'syslog') { - $rows = syslog_db_fetch_assoc("SELECT * + $rows = syslog_db_fetch_assoc_prepared("SELECT * FROM `" . $syslogdb_default . "`.`syslog_remove` WHERE enabled = 'on' - AND id = $uniqueID"); + AND id <= ?", array($max_seq)); } else { $rows = syslog_db_fetch_assoc('SELECT * FROM `' . $syslogdb_default . '`.`syslog_remove` @@ -331,9 +322,11 @@ function syslog_remove_items($table, $uniqueID) { $xferred = 0; if ($table == 'syslog_incoming') { - $total = syslog_db_fetch_cell('SELECT count(*) + $total = syslog_db_fetch_cell_prepared('SELECT count(*) FROM `' . $syslogdb_default . '`.`syslog_incoming` - WHERE `status` = ' . $uniqueID); + WHERE `status` = 1 + AND `seq` <= ?', + array($max_seq)); } else { $total = 0; } @@ -342,788 +335,103 @@ function syslog_remove_items($table, $uniqueID) { foreach($rows as $remove) { $sql = ''; $sql1 = ''; + $sql2 = ''; - if ($remove['type'] == 'facility') { - if ($table == 'syslog_incoming') { - if ($remove['method'] != 'del') { - $sql1 = 'INSERT INTO `' . $syslogdb_default . '`.`syslog_removed` - (logtime, priority_id, facility_id, program_id, host_id, message) - SELECT logtime, priority_id, facility_id, program_id, host_id, message - FROM (SELECT si.logtime, si.priority_id, si.facility_id, spg.program_id, sh.host_id, si.message - FROM `' . $syslogdb_default . '`.`syslog_incoming` AS si - INNER JOIN `' . $syslogdb_default . '`.`syslog_facilities` AS sf - ON sf.facility_id = si.facility_id - INNER JOIN `' . $syslogdb_default . '`.`syslog_priorities` AS sp - ON sp.priority_id = si.priority_id - INNER JOIN `' . $syslogdb_default . '`.`syslog_programs` AS spg - ON spg.program = si.program - INNER JOIN `' . $syslogdb_default . '`.`syslog_hosts` AS sh - ON sh.host = si.host - WHERE ' . $syslog_incoming_config['facilityField'] . ' = ' . db_qstr($remove['message']) . ' - AND `status` = ' . $uniqueID . ' - ) AS merge'; - } - - $sql = 'DELETE - FROM `' . $syslogdb_default . '`.`syslog_incoming` - WHERE ' . $syslog_incoming_config['facilityField'] . ' = ' . db_qstr($remove['message']) . ' - AND `status` = ' . $uniqueID; - } else { - $facility_id = syslog_db_fetch_cell('SELECT facility_id - FROM `' . $syslogdb_default . '`.`syslog_facilities` - WHERE facility = ' . db_qstr($remove['message'])); - - if (!empty($facility_id)) { - if ($remove['method'] != 'del') { - $sql1 = 'INSERT INTO `' . $syslogdb_default . '`.`syslog_removed` - (logtime, priority_id, facility_id, program_id, host_id, message) - SELECT logtime, priority_id, facility_id, program_id, host_id, message - FROM `' . $syslogdb_default . '`.`syslog` - WHERE facility_id = ' . $facility_id; - } + $params = array(); - $sql = 'DELETE FROM `' . $syslogdb_default . '`.`syslog` - WHERE facility_id = ' . $facility_id; - } - } - } else if ($remove['type'] == 'program') { - if ($table == 'syslog_incoming') { - if ($remove['method'] != 'del') { - $sql1 = 'INSERT INTO `' . $syslogdb_default . '`.`syslog_removed` - (logtime, priority_id, facility_id, program_id, host_id, message) - SELECT logtime, priority_id, facility_id, program_id, host_id, message - FROM (SELECT si.logtime, si.priority_id, si.facility_id, spg.program_id, sh.host_id, si.message - FROM `' . $syslogdb_default . '`.`syslog_incoming` AS si - INNER JOIN `' . $syslogdb_default . '`.`syslog_facilities` AS sf - ON sf.facility_id = si.facility_id - INNER JOIN `' . $syslogdb_default . '`.`syslog_priorities` AS sp - ON sp.priority_id = si.priority_id - INNER JOIN `' . $syslogdb_default . '`.`syslog_programs` AS spg - ON spg.program = si.program - INNER JOIN `' . $syslogdb_default . '`.`syslog_hosts` AS sh - ON sh.host = si.host - WHERE program = ' . db_qstr($remove['message']) . ' - AND `status` = ' . $uniqueID . ' - ) AS merge'; - } - - $sql = 'DELETE - FROM `' . $syslogdb_default . '`.`syslog_incoming` - WHERE `program` = ' . db_qstr($remove['message']) . ' - AND `status` = ' . $uniqueID; - } else { - $program_id = syslog_db_fetch_cell('SELECT program_id - FROM `' . $syslogdb_default . '`.`syslog_programs` - WHERE program = ' . db_qstr($remove['message'])); - - if (!empty($program_id)) { - if ($remove['method'] != 'del') { - $sql1 = 'INSERT INTO `' . $syslogdb_default . '`.`syslog_removed` - (logtime, priority_id, facility_id, program_id, host_id, message) - SELECT logtime, priority_id, facility_id, program_id, host_id, message - FROM `' . $syslogdb_default . '`.`syslog` - WHERE program_id = ' . $program_id; - } - - $sql = 'DELETE FROM `' . $syslogdb_default . '`.`syslog` - WHERE program_id = ' . $program_id; - } - } - } elseif ($remove['type'] == 'host') { - if ($table == 'syslog_incoming') { - if ($remove['method'] != 'del') { - $sql1 = 'INSERT INTO `' . $syslogdb_default . '`.`syslog_removed` - (logtime, priority_id, facility_id, program_id, host_id, message) - SELECT logtime, priority_id, facility_id, program_id, host_id, message - FROM (SELECT si.logtime, si.priority_id, si.facility_id, spg.program_id, sh.host_id, si.message - FROM `' . $syslogdb_default . '`.`syslog_incoming` AS si - INNER JOIN `' . $syslogdb_default . '`.`syslog_facilities` AS sf - ON sf.facility_id = si.facility_id - INNER JOIN `' . $syslogdb_default . '`.`syslog_priorities` AS sp - ON sp.priority_id = si.priority_id - INNER JOIN `' . $syslogdb_default . '`.`syslog_programs` AS spg - ON spg.program = si.program - INNER JOIN `' . $syslogdb_default . '`.`syslog_hosts` AS sh - ON sh.host = si.host - WHERE si.host = ' . db_qstr($remove['message']) . ' - AND `status` = ' . $uniqueID . ' - ) AS merge'; - } - - $sql = 'DELETE - FROM `' . $syslogdb_default . '`.`syslog_incoming` - WHERE host = ' . db_qstr($remove['message']) . ' - AND `status` = ' . $uniqueID; - } else { - $host_id = syslog_db_fetch_cell('SELECT host_id - FROM `' . $syslogdb_default . '`.`syslog_hosts` - WHERE host = ' . db_qstr($remove['message'])); - - if (!empty($host_id)) { - if ($remove['method'] != 'del') { - $sql1 = 'INSERT INTO `' . $syslogdb_default . '`.`syslog_removed` - (logtime, priority_id, facility_id, program_id, host_id, message) - SELECT logtime, priority_id, facility_id, program_id, host_id, message - FROM `' . $syslogdb_default . '`.`syslog` - WHERE host_id = ' . $host_id; - } + if ($remove['type'] == 'facility') { + $sql = 'FROM `' . $syslogdb_default . '`.`syslog_incoming` + WHERE `' . $syslog_incoming_config['facilityField'] . '` = ? + AND `status` = 1 + AND `seq` <= ?'; - $sql = 'DELETE FROM `' . $syslogdb_default . '`.`syslog` - WHERE host_id = ' . $host_id; - } - } + $params[] = $remove['message']; + $params[] = $max_seq; } elseif ($remove['type'] == 'messageb') { - if ($table == 'syslog_incoming') { - if ($remove['method'] != 'del') { - $sql1 = 'INSERT INTO `' . $syslogdb_default . '`.`syslog_removed` - (logtime, priority_id, facility_id, program_id, host_id, message) - SELECT logtime, priority_id, facility_id, program_id, host_id, message - FROM (SELECT si.logtime, si.priority_id, si.facility_id, spg.program_id, sh.host_id, si.message - FROM `' . $syslogdb_default . '`.`syslog_incoming` AS si - INNER JOIN `' . $syslogdb_default . '`.`syslog_facilities` AS sf - ON sf.facility_id = si.facility_id - INNER JOIN `' . $syslogdb_default . '`.`syslog_priorities` AS sp - ON sp.priority_id = si.priority_id - INNER JOIN `' . $syslogdb_default . '`.`syslog_programs` AS spg - ON spg.program = si.program - INNER JOIN `' . $syslogdb_default . '`.`syslog_hosts` AS sh - ON sh.host = si.host - WHERE message LIKE ' . db_qstr($remove['message'] . '%') . ' - AND `status` = ' . $uniqueID . ' - ) AS merge'; - } - - $sql = 'DELETE - FROM `' . $syslogdb_default . '`.`syslog_incoming` - WHERE message LIKE ' . db_qstr($remove['message'] . '%') . ' - AND `status` = ' . $uniqueID; - } else { - if ($remove['message'] != '') { - if ($remove['method'] != 'del') { - $sql1 = 'INSERT INTO `' . $syslogdb_default . '`.`syslog_removed` - (logtime, priority_id, facility_id, program_id, host_id, message) - SELECT logtime, priority_id, facility_id, program_id, host_id, message - FROM `' . $syslogdb_default . '`.`syslog` - WHERE message LIKE ' . db_qstr($remove['message'] . '%'); - } + $sql = 'FROM `' . $syslogdb_default . '`.`syslog_incoming` + WHERE `' . $syslog_incoming_config['textField'] . '` LIKE ? + AND `status` = 1 + AND `seq` <= ?'; - $sql = 'DELETE FROM `' . $syslogdb_default . '`.`syslog` - WHERE message LIKE ' . db_qstr($remove['message'] . '%'); - } - } + $params[] = $remove['message'] . '%'; + $params[] = $max_seq; } elseif ($remove['type'] == 'messagec') { - if ($table == 'syslog_incoming') { - if ($remove['method'] != 'del') { - $sql1 = 'INSERT INTO `' . $syslogdb_default . '`.`syslog_removed` - (logtime, priority_id, facility_id, program_id, host_id, message) - SELECT logtime, priority_id, facility_id, program_id, host_id, message - FROM (SELECT si.logtime, si.priority_id, si.facility_id, spg.program_id, sh.host_id, si.message - FROM `' . $syslogdb_default . '`.`syslog_incoming` AS si - INNER JOIN `' . $syslogdb_default . '`.`syslog_facilities` AS sf - ON sf.facility_id = si.facility_id - INNER JOIN `' . $syslogdb_default . '`.`syslog_priorities` AS sp - ON sp.priority_id = si.priority_id - INNER JOIN `' . $syslogdb_default . '`.`syslog_programs` AS spg - ON spg.program = si.program - INNER JOIN `' . $syslogdb_default . '`.`syslog_hosts` AS sh - ON sh.host = si.host - WHERE message LIKE ' . db_qstr('%' . $remove['message'] . '%') . ' - AND `status` = ' . $uniqueID . ' - ) AS merge'; - } + $sql = 'FROM `' . $syslogdb_default . '`.`syslog_incoming` + WHERE `' . $syslog_incoming_config['textField'] . '` LIKE ? + AND `status` = 1 + AND `seq` <= ?'; - $sql = 'DELETE - FROM `' . $syslogdb_default . '`.`syslog_incoming` - WHERE message LIKE ' . db_qstr('%' . $remove['message'] . '%') . ' - AND `status` = ' . $uniqueID; - } else { - if ($remove['message'] != '') { - if ($remove['method'] != 'del') { - $sql1 = 'INSERT INTO `' . $syslogdb_default . '`.`syslog_removed` - (logtime, priority_id, facility_id, program_id, host_id, message) - SELECT logtime, priority_id, facility_id, program_id, host_id, message - FROM `' . $syslogdb_default . '`.`syslog` - WHERE message LIKE ' . db_qstr('%' . $remove['message'] . '%'); - } - - $sql = 'DELETE FROM `' . $syslogdb_default . '`.`syslog` - WHERE message LIKE ' . db_qstr('%' . $remove['message'] . '%'); - } - } + $params[] = '%' . $remove['message'] . '%'; + $params[] = $max_seq; } elseif ($remove['type'] == 'messagee') { - if ($table == 'syslog_incoming') { - if ($remove['method'] != 'del') { - $sql1 = 'INSERT INTO `' . $syslogdb_default . '`.`syslog_removed` - (logtime, priority_id, facility_id, program_id, host_id, message) - SELECT logtime, priority_id, facility_id, program_id, host_id, message - FROM (SELECT si.logtime, si.priority_id, si.facility_id, spg.program_id, sh.host_id, si.message - FROM `' . $syslogdb_default . '`.`syslog_incoming` AS si - INNER JOIN `' . $syslogdb_default . '`.`syslog_facilities` AS sf - ON sf.facility_id = si.facility_id - INNER JOIN `' . $syslogdb_default . '`.`syslog_priorities` AS sp - ON sp.priority_id = si.priority_id - INNER JOIN `' . $syslogdb_default . '`.`syslog_programs` AS spg - ON spg.program = si.program - INNER JOIN `' . $syslogdb_default . '`.`syslog_hosts` AS sh - ON sh.host = si.host - WHERE message LIKE ' . db_qstr('%' . $remove['message']) . ' - AND `status` = ' . $uniqueID . ' - ) AS merge'; - } + $sql = 'FROM `' . $syslogdb_default . '`.`syslog_incoming` + WHERE `' . $syslog_incoming_config['textField'] . '` LIKE ? + AND `status` = 1 + AND `seq` <= ?'; - $sql = 'DELETE - FROM `' . $syslogdb_default . '`.`syslog_incoming` - WHERE message LIKE ' . db_qstr('%' . $remove['message']) . ' - AND `status` = ' . $uniqueID; - } else { - if ($remove['message'] != '') { - if ($remove['method'] != 'del') { - $sql1 = 'INSERT INTO `' . $syslogdb_default . '`.`syslog_removed` - (logtime, priority_id, facility_id, program_id, host_id, message) - SELECT logtime, priority_id, facility_id, program_id, host_id, message - FROM `' . $syslogdb_default . '`.`syslog` - WHERE message LIKE ' . db_qstr('%' . $remove['message']); - } - - $sql = 'DELETE FROM `' . $syslogdb_default . '`.`syslog` - WHERE message LIKE ' . db_qstr('%' . $remove['message']); - } - } + $params[] = '%' . $remove['message']; + $params[] = $max_seq; + } elseif ($remove['type'] == 'host') { + $sql = 'FROM `' . $syslogdb_default . '`.`syslog_incoming` + WHERE `' . $syslog_incoming_config['hostField'] . '` = ? + AND `status` = 1 + AND `seq` <= ?'; + + $params[] = $remove['message']; + $params[] = $max_seq; + } elseif ($remove['type'] == 'program') { + $sql = 'FROM `' . $syslogdb_default . '`.`syslog_incoming` + WHERE `' . $syslog_incoming_config['programField'] . '` = ? + AND `status` = 1 + AND `seq` <= ?'; + + $params[] = $remove['message']; + $params[] = $max_seq; } elseif ($remove['type'] == 'sql') { - if ($table == 'syslog_incoming') { - if ($remove['method'] != 'del') { - $sql1 = 'INSERT INTO `' . $syslogdb_default . '`.`syslog_removed` - (logtime, priority_id, facility_id, program_id, host_id, message) - SELECT logtime, priority_id, facility_id, program_id, host_id, message - FROM (SELECT si.logtime, si.priority_id, si.facility_id, spg.program_id, sh.host_id, si.message - FROM `' . $syslogdb_default . '`.`syslog_incoming` AS si - INNER JOIN `' . $syslogdb_default . '`.`syslog_facilities` AS sf - ON sf.facility_id = si.facility_id - INNER JOIN `' . $syslogdb_default . '`.`syslog_priorities` AS sp - ON sp.priority_id = si.priority_id - INNER JOIN `' . $syslogdb_default . '`.`syslog_programs` AS spg - ON spg.program = si.program - INNER JOIN `' . $syslogdb_default . '`.`syslog_hosts` AS sh - ON sh.host = si.host - WHERE `status` = ' . $uniqueID . ' - AND (' . $remove['message'] . ') - ) AS merge'; - } + $sql = 'FROM `' . $syslogdb_default . '`.`syslog_incoming` + WHERE (' . $remove['message'] . ') + AND `status` = 1 + AND `seq` <= ?'; - $sql = 'DELETE - FROM `' . $syslogdb_default . '`.`syslog_incoming` - WHERE (' . $remove['message'] . ') - AND `status` = ' . $uniqueID; - } else { - if ($remove['message'] != '') { - if ($remove['method'] != 'del') { - $sql1 = 'INSERT INTO `' . $syslogdb_default . '`.`syslog_removed` - (logtime, priority_id, facility_id, program_id, host_id, message) - SELECT logtime, priority_id, facility_id, program_id, host_id, message - FROM `' . $syslogdb_default . '`.`syslog` - WHERE ' . $remove['message']; - } - - $sql = 'DELETE - FROM `' . $syslogdb_default . '`.`syslog` - WHERE ' . $remove['message']; - } - } + $params[] = $max_seq; } - if ($sql != '' || $sql1 != '') { - $debugm = ''; - /* process the removal rule first */ - if ($sql1 != '') { - /* now delete the remainder that match */ - syslog_db_execute($sql1); - } - - /* now delete the remainder that match */ - syslog_db_execute($sql); - $removed += db_affected_rows($syslog_cnn); - $debugm = sprintf('Deleted %5s - ', $removed); - if ($sql1 != '') { + if ($sql != '') { + if ($remove['action'] == 1) { + /* remove them */ + $sql1 = 'DELETE ' . $sql; + syslog_db_execute_prepared($sql1, $params); + $removed += db_affected_rows($syslog_cnn); + } elseif ($remove['action'] == 2) { + /* transfer them */ + $sql1 = 'INSERT INTO `' . $syslogdb_default . '`.`syslog` + (logtime, priority_id, facility_id, program_id, host_id, message) + SELECT logtime, priority_id, facility_id, sp.program_id, sh.host_id, message + FROM ( + SELECT logtime, priority_id, facility_id, sp.program_id, sh.host_id, message ' . $sql . ' + INNER JOIN syslog_hosts AS sh + ON sh.host = syslog_incoming.host + INNER JOIN syslog_programs AS sp + ON sp.program = syslog_incoming.program + ) AS merge'; + + syslog_db_execute_prepared($sql1, $params); $xferred += db_affected_rows($syslog_cnn); - $debugm = sprintf('Moved %5s - ', $xferred); - } - syslog_debug($debugm . 'Message' . (db_affected_rows($syslog_cnn) == 1 ? '' : 's' ) . - " for removal rule '" . $remove['name'] . "'"); - } - } - } - - return array('removed' => $removed, 'xferred' => $xferred); -} - -/** function syslog_log_row_color() - * This function set's the CSS for each row of the syslog table as it is displayed - * it supports both the legacy as well as the new approach to controlling these - * colors. -*/ -function syslog_log_row_color($severity, $tip_title) { - switch($severity) { - case '': - case '0': - $class = 'logInfo'; - break; - case '1': - $class = 'logWarning'; - break; - case '2': - $class = 'logAlert'; - break; - } - - print "\n"; -} - -/** function syslog_row_color() - * This function set's the CSS for each row of the syslog table as it is displayed - * it supports both the legacy as well as the new approach to controlling these - * colors. -*/ -function syslog_row_color($priority, $message) { - switch($priority) { - case '0': - $class = 'logEmergency'; - break; - case '1': - $class = 'logAlert'; - break; - case '2': - $class = 'logCritical'; - break; - case '3': - $class = 'logError'; - break; - case '4': - $class = 'logWarning'; - break; - case '5': - $class = 'logNotice'; - break; - case '6': - $class = 'logInfo'; - break; - case '7': - $class = 'logDebug'; - break; - } - - print ""; - return $class; -} - -function sql_hosts_where($tab) { - global $hostfilter, $hostfilter_log, $syslog_incoming_config; - global $syslogdb_default; - - $hostfilter = ''; - $hostfilter_log = ''; - $hosts_array = array(); - - if (!isempty_request_var('host') && get_nfilter_request_var('host') != 'null') { - $hostarray = explode(',', trim(get_nfilter_request_var('host'))); - if ($hostarray[0] != '0') { - foreach($hostarray as $host_id) { - input_validate_input_number($host_id); - - if ($host_id > 0) { - $log_host = syslog_db_fetch_cell_prepared('SELECT host - FROM `' . $syslogdb_default . '`.`syslog_hosts` - WHERE host_id = ?', - array($host_id)); - - if (!empty($log_host)) { - $hosts_array[] = db_qstr($log_host); - } - } - } - - if (cacti_sizeof($hosts_array)) { - $hostfilter_log = ' host IN(' . implode(',', $hosts_array) . ')'; - } - - $hostfilter .= ($hostfilter != '' ? ' AND ':'') . ' host_id IN(' . implode(',', $hostarray) . ')'; - } - } -} - -function syslog_export($tab) { - global $syslog_incoming_config, $severities; - global $syslogdb_default; - - if (defined('SYSLOG_CONFIG')) { - include(SYSLOG_CONFIG); - } - - if ($tab == 'syslog') { - header('Content-type: application/excel'); - header('Content-Disposition: attachment; filename=syslog_view-' . date('Y-m-d',time()) . '.csv'); - - $sql_where = ''; - $messages = get_syslog_messages($sql_where, 100000, $tab); - - $hosts = array_rekey( - syslog_db_fetch_assoc('SELECT host_id, host - FROM `' . $syslogdb_default . '`.`syslog_hosts`'), - 'host_id', 'host' - ); - - $facilities = array_rekey( - syslog_db_fetch_assoc('SELECT facility_id, facility - FROM `' . $syslogdb_default . '`.`syslog_facilities`'), - 'facility_id', 'facility' - ); - - $priorities = array_rekey( - syslog_db_fetch_assoc('SELECT priority_id, priority - FROM `' . $syslogdb_default . '`.`syslog_priorities`'), - 'priority_id', 'priority' - ); - - $programs = array_rekey( - syslog_db_fetch_assoc('SELECT program_id, program - FROM `' . $syslogdb_default . '`.`syslog_programs`'), - 'program_id', 'program' - ); - - print 'host, facility, priority, program, date, message' . "\r\n"; - - if (cacti_sizeof($messages)) { - foreach ($messages as $message) { - if (isset($facilities[$message['facility_id']])) { - $facility = $facilities[$message['facility_id']]; - } else { - $facility = 'Unknown'; + $sql2 = 'DELETE ' . $sql; + syslog_db_execute_prepared($sql2, $params); } - - if (isset($programs[$message['program_id']])) { - $program = $programs[$message['program_id']]; - } else { - $program = 'Unknown'; - } - - if (isset($priorities[$message['priority_id']])) { - $priority = $priorities[$message['priority_id']]; - } else { - $priority = 'Unknown'; - } - - if (isset($hosts[$message['host_id']])) { - $host = $hosts[$message['host_id']]; - } else { - $host = 'Unknown'; - } - - print - '"' . - $host . '","' . - ucfirst($facility) . '","' . - ucfirst($priority) . '","' . - ucfirst($program) . '","' . - $message['logtime'] . '","' . - $message[$syslog_incoming_config['textField']] . '"' . "\r\n"; - } - } - } else { - header('Content-type: application/excel'); - header('Content-Disposition: attachment; filename=alert_log_view-' . date('Y-m-d',time()) . '.csv'); - - $sql_where = ''; - $messages = get_syslog_messages($sql_where, 100000, $tab); - - print 'name, severity, date, message, host, facility, priority, count' . "\r\n"; - - if (cacti_sizeof($messages)) { - foreach ($messages as $message) { - if (isset($severities[$message['severity']])) { - $severity = $severities[$message['severity']]; - } else { - $severity = 'Unknown'; - } - - print - '"' . - $message['name'] . '","' . - $severity . '","' . - $message['logtime'] . '","' . - $message['logmsg'] . '","' . - $message['host'] . '","' . - ucfirst($message['facility']) . '","' . - ucfirst($message['priority']) . '","' . - $message['count'] . '"' . "\r\n"; - } - } - } -} - -function syslog_debug($message) { - global $debug; - - if ($debug) { - print date('H:m:s') . ' SYSLOG DEBUG: ' . trim($message) . PHP_EOL; - } -} - -function syslog_log_alert($alert_id, $alert_name, $severity, $msg, $count = 1, $html = '', $hosts = array()) { - global $config, $severities; - global $syslogdb_default; - - if ($count <= 1) { - $save['seq'] = ''; - $save['alert_id'] = $alert_id; - $save['logseq'] = $msg['seq']; - $save['logtime'] = $msg['logtime']; - $save['logmsg'] = $msg['message']; - $save['host'] = $msg['host']; - $save['facility_id'] = $msg['facility_id']; - $save['priority_id'] = $msg['priority_id']; - $save['count'] = 1; - $save['html'] = $html; - - $id = 0; - $id = syslog_sql_save($save, '`' . $syslogdb_default . '`.`syslog_logs`', 'seq'); - - $save['seq'] = $id; - $save['alert_name'] = $alert_name; - api_plugin_hook_function('syslog_update_hostsalarm', $save); - - cacti_log("WARNING: The Syslog Alert '$alert_name' with Severity '" . $severities[$severity] . "', has been Triggered on Host '" . $msg['host'] . "', and Sequence '$id'", false, 'SYSLOG'); - - return $id; - } else { - $save['seq'] = ''; - $save['alert_id'] = $alert_id; - $save['logseq'] = 0; - $save['logtime'] = date('Y-m-d H:i:s'); - $save['logmsg'] = $alert_name; - $save['host'] = 'N/A'; - $save['facility_id'] = $msg['facility_id']; - $save['priority_id'] = $msg['priority_id']; - $save['count'] = $count; - $save['html'] = $html; - - $id = 0; - $id = syslog_sql_save($save, '`' . $syslogdb_default . '`.`syslog_logs`', 'seq'); - - $save['seq'] = $id; - $save['alert_name'] = $alert_name; - - if (cacti_sizeof($hosts)) { - foreach($hosts as $host) { - $save['host'] = $host; - api_plugin_hook_function('syslog_update_hostsalarm', $save); } } - - cacti_log("WARNING: The Syslog Instance Alert '$alert_name' with Severity '" . $severities[$severity] . "', has been Triggered, Count was '" . $count . "', and Sequence '$id'", false, 'SYSLOG'); - - return $id; } -} - -function syslog_manage_items($from_table, $to_table) { - global $config, $syslog_cnn, $syslog_incoming_config; - global $syslogdb_default; - - /* Select filters to work on */ - $rows = syslog_db_fetch_assoc('SELECT * FROM `' . $syslogdb_default . "`.`syslog_remove` WHERE enabled='on'"); - - syslog_debug(sprintf('Found %5s - Removal Rule(s) to process', cacti_sizeof($rows))); - - $removed = 0; - $xferred = 0; - $total = 0; - - if (cacti_sizeof($rows)) { - foreach($rows as $remove) { - syslog_debug('Processing Rule - ' . $remove['message']); - - $sql_sel = ''; - $sql_dlt = ''; - - if ($remove['type'] == 'facility') { - if ($remove['method'] != 'del') { - $sql_sel = "SELECT seq FROM `" . $syslogdb_default . "`. $from_table - WHERE facility_id IN - (SELECT distinct facility_id FROM `". $syslogdb_default . "`syslog_facilities - WHERE facility ='". $remove['message']."')"; - } else { - $sql_dlt = "DELETE FROM `" . $syslogdb_default . "`. $from_table - WHERE facility_id IN - (SELECT distinct facility_id FROM `". $syslogdb_default . "`syslog_facilities - WHERE facility ='". $remove['message']."')"; - } - - } elseif ($remove['type'] == 'host') { - if ($remove['method'] != 'del') { - $sql_sel = "SELECT seq - FROM `" . $syslogdb_default . "`. $from_table - WHERE host_id in - (SELECT distinct host_id FROM `". $syslogdb_default . "`syslog_hosts - WHERE host ='". $remove['message']."')"; - } else { - $sql_dlt = "DELETE FROM `" . $syslogdb_default . "`. $from_table - WHERE host_id in - (SELECT distinct host_id FROM `". $syslogdb_default . "`syslog_hosts - WHERE host ='". $remove['message']."')"; - } - } elseif ($remove['type'] == 'messageb') { - if ($remove['method'] != 'del') { - $sql_sel = "SELECT seq FROM `" . $syslogdb_default . "`. $from_table - WHERE message LIKE '" . $remove['message'] . "%' "; - } else { - $sql_dlt = "DELETE FROM `" . $syslogdb_default . "`. $from_table - WHERE message LIKE '" . $remove['message'] . "%' "; - } - - } elseif ($remove['type'] == 'messagec') { - if ($remove['method'] != 'del') { - $sql_sel = "SELECT seq FROM `" . $syslogdb_default . "`. $from_table - WHERE message LIKE '%" . $remove['message'] . "%' "; - } else { - $sql_dlt = "DELETE FROM `" . $syslogdb_default . "`. $from_table - WHERE message LIKE '%" . $remove['message'] . "%' "; - } - } elseif ($remove['type'] == 'messagee') { - if ($remove['method'] != 'del') { - $sql_sel = "SELECT seq FROM `" . $syslogdb_default . "`. $from_table - WHERE message LIKE '%" . $remove['message'] . "' "; - } else { - $sql_dlt = "DELETE FROM `" . $syslogdb_default . "`. $from_table - WHERE message LIKE '%" . $remove['message'] . "' "; - } - } elseif ($remove['type'] == 'sql') { - if ($remove['method'] != 'del') { - $sql_sel = "SELECT seq FROM `" . $syslogdb_default . "`. $from_table - WHERE message (" . $remove['message'] . ") "; - } else { - $sql_dlt = "DELETE FROM `" . $syslogdb_default . "`. $from_table - WHERE message (" . $remove['message'] . ") "; - } - } - - if ($sql_sel != '' || $sql_dlt != '') { - $debugm = ''; - /* process the removal rule first */ - if ($sql_sel != '') { - $move_count = 0; - /* first insert, then delete */ - $move_records = syslog_db_fetch_assoc($sql_sel); - syslog_debug(sprintf('Found %5s - Message(s)', cacti_sizeof($move_records))); - - if (cacti_sizeof($move_records)) { - $all_seq = ''; - $messages_moved = 0; - foreach($move_records as $move_record) { - $all_seq = $all_seq . ", " . $move_record['seq']; - } - - $all_seq = preg_replace('/^,/i', '', $all_seq); - syslog_db_execute("INSERT INTO `". $syslogdb_default . "`.`". $to_table ."` - (facility_id, priority_id, host_id, logtime, message) - (SELECT facility_id, priority_id, host_id, logtime, message - FROM `". $syslogdb_default . "`.". $from_table ." - WHERE seq IN (" . $all_seq ."))"); - - $messages_moved = db_affected_rows($syslog_cnn); - - if ($messages_moved > 0) { - syslog_db_execute("DELETE FROM `". $syslogdb_default . "`.`" . $from_table ."` - WHERE seq IN (" . $all_seq .")" ); - } - - $xferred += $messages_moved; - $move_count = $messages_moved; - } - - $debugm = sprintf('Moved %5s - Message(s)', $move_count); - } - - if ($sql_dlt != '') { - /* now delete the remainder that match */ - syslog_db_execute($sql_dlt); - $removed += db_affected_rows($syslog_cnn); - $debugm = sprintf('Deleted %5s Message(s)', $removed); - } - syslog_debug($debugm); - } - } - } + syslog_debug(sprintf('Removed %5s - Record(s) from incoming', $removed)); + syslog_debug(sprintf('Xferred %5s - Record(s) to the syslog table', $xferred)); return array('removed' => $removed, 'xferred' => $xferred); } -/* get_hash_syslog - returns the current unique hash for an alert - @arg $id - (int) the ID of the syslog item to return a hash for - @returns - a 128-bit, hexadecimal hash */ -function get_hash_syslog($id, $table) { - $hash = syslog_db_fetch_cell_prepared('SELECT hash - FROM ' . $table . ' - WHERE id = ?', - array($id)); - - if (empty($hash)) { - return generate_hash(); - } elseif (preg_match('/[a-fA-F0-9]{32}/', $hash)) { - return $hash; - } else { - return generate_hash(); - } -} - -function syslog_ia2xml($array) { - $xml = ''; - - if (cacti_sizeof($array)) { - foreach ($array as $key=>$value) { - if (is_array($value)) { - $xml .= "\t<$key>" . syslog_ia2xml($value) . "\n"; - } else { - $xml .= "\t<$key>" . html_escape($value) . "\n"; - } - } - } - - return $xml; -} - -function syslog_array2xml($array, $tag = 'template') { - static $index = 1; - - $xml = "<$tag$index>\n" . syslog_ia2xml($array) . "\n"; - - $index++; - - return $xml; -} - -/** - * syslog_process_alerts - Process each of the Syslog Alerts - * - * Syslog Alerts come in essentially 4 types - * - * System Wide non-threshold alerts - These alerts are simply alerts that match the pattern defined by the alert - * System Wide threshold alerts - These alerts are syslog messages that both match the pattern and have more than the - * threshold amount that take place every collector cycle (30 seconds, 1 minutes, 5 minutes, etc) - * Host based non-threshold alerts - Alerts that happen on a per host basis, so you can alert for each host that the syslog message - * occurred to. - * Host based threshold alerts - Like the system level alert, it's an alert that happens more than x times per host. - * - * The advantage and reason for having host based alerts is that it allows you to target ticket generation for a specific host - * and more importantly, to be able to have a separate re-alert cycles for that very same message as there can be similar messages - * happening all the time at the system level, so it's hard to target a single host for re-alert rules. - * - * @param (int) The unique id to process - * - * @return (array) An array of the number of alerts processed and the number of alerts generated - */ -function syslog_process_alerts($uniqueID) { - global $syslogdb_default; +function syslog_process_alerts($max_seq) { global $syslogdb_default; $syslog_alarms = 0; @@ -1147,7 +455,6 @@ function syslog_process_alerts($uniqueID) { if (cacti_sizeof($alerts)) { foreach($alerts as $alert) { $sql = ''; - $th_sql = ''; $params = array(); /* we roll up statistics depending on the level */ @@ -1157,7 +464,7 @@ function syslog_process_alerts($uniqueID) { $groupBy = ''; } - $sql_data = syslog_get_alert_sql($alert, $uniqueID); + $sql_data = syslog_get_alert_sql($alert, $max_seq); if (!cacti_sizeof($sql_data)) { syslog_debug(sprintf('Error - Unable to determine SQL for Alert \'%s\'', $alert['name'])); @@ -1167,478 +474,26 @@ function syslog_process_alerts($uniqueID) { $sql = $sql_data['sql']; $params = $sql_data['params']; - /** - * For this next step in processing, we want to call the syslog_process_alert - * once for every host, or system level breach that is encountered. This removes - * must of the complexity that would otherwise go into the syslog_process_alert - * function. - */ if ($sql != '') { - if ($alert['level'] == '1') { - /** - * This is a host level alert process each host separately - * both thresholed and system levels have the same process - */ - $th_sql = str_replace('*', 'host, COUNT(*) AS count', $sql); - $results = syslog_db_fetch_assoc_prepared($th_sql . $groupBy, $params); - - if (cacti_sizeof($results)) { - foreach($results as $result) { - $aparams = $params; - $aparams[] = $result['host']; - - $asql = $sql . ' AND host = ?'; - - $syslog_alarms += syslog_process_alert($alert, $asql, $aparams, $result['count'], $result['host']); - } - } - } elseif ($alert['method'] == '1') { - /** - * This is a system level threshold breach - */ - $th_sql = str_replace('*', 'COUNT(*)', $sql); - $count = syslog_db_fetch_cell_prepared($th_sql . $groupBy, $params); - $syslog_alarms += syslog_process_alert($alert, $sql, $params, $count); - } else { - /** - * This is a system level classic syslog breach without a threshold - */ - $count = 0; - $syslog_alarms += syslog_process_alert($alert, $sql, $params, $count); - } - } - } - } + $results = syslog_db_fetch_assoc_prepared($sql . $groupBy, $params); - return array('syslog_alerts' => $syslog_alerts, 'syslog_alarms' => $syslog_alarms); -} + if (cacti_sizeof($results)) { + foreach($results as $result) { + $syslog_alarms++; + syslog_debug(sprintf('Alert - Alert \'%s\' matched', $alert['name'])); -/** - * syslog_process_alert - Process the Alert and generate notifications, execute commands, etc. - * - * @param (array) The alert to process - * @param (string) The SQL to search for the Alert - * @param (array) The SQL parameters to be prepared into the SQL - * @param (int) In the case of a threshold alert, the number of occurrents - * of hosts with occurrences that were encountered through - * pre-processing the message - * @param (string) The hostname that this alert rule is for - * - * @return (int) '1' if the alert triggered, else '0' - */ -function syslog_process_alert($alert, $sql, $params, $count, $hostname = '') { - global $config, $severities, $syslog_levels; - - include_once($config['base_path'] . '/lib/reports.php'); - - $messese = ''; - $smsalert = ''; - - $alert_count = 0; - $syslog_alarms = 0; - $hostlist = array(); - $max_alerts = read_config_option('syslog_maxrecords'); - $report_tag = false; - $theme = false; - $format_ok = false; - - syslog_debug('-------------------------------------------------------------------------------------'); - syslog_debug(sprintf('Processing - %s', $alert['name'])); - - if (read_config_option('syslog_html') == 'on') { - $html = true; - $format_ok = reports_load_format_file(read_config_option('syslog_format_file'), $output, $report_tag, $theme); - - syslog_debug('Format/CSS ' . ($format_ok ? 'Ok':'Not Ok') . ' - Report Tag ' . ($report_tag ? 'included':'missing')); - } else { - $html = false; - } - - /** - * format the from Email address - */ - $from_email = read_config_option('settings_from_email'); - if ($from_email == '') { - $from_email = 'Cacti@cacti.net'; - } - - $from_name = read_config_option('settings_from_name'); - if ($from_name == '') { - $from_name = 'Cacti Reporting'; - } - - $from = array($from_email, $from_name); - - /** - * format the destination Email addresses - */ - $alert['email'] = trim($alert['email'], ', '); - if ($alert['notify'] > 0) { - $additional = db_fetch_cell_prepared('SELECT emails - FROM plugin_notification_lists - WHERE id = ?', - array($alert['notify'])); - - if ($additional != '') { - $alert['email'] .= ', ' . trim($additional, ' ,'); - } - } - - /** - * process the alert now. - */ - if (($alert['method'] == '1' && $count >= $alert['num']) || $alert['method'] == '0') { - $at = syslog_db_fetch_assoc_prepared($sql, $params); - - /** - * get a date for the repeat alert - */ - if ($alert['repeat_alert']) { - $date = date('Y-m-d H:i:s', time() - ($alert['repeat_alert'] * read_config_option('poller_interval'))); - } else { - $date = ''; - } - - /** - * The finalized email or test message. - */ - $message = ''; - - /** - * A list of all messages from the alert - */ - $results = array(); - - syslog_debug(sprintf('Found %5s - Matching Records.', cacti_sizeof($at))); - - if (cacti_sizeof($at)) { - if ($html) { - if (!$format_ok) { - $message .= "'; - } - - if ($alert['method'] == '1') { - if ($alert['body'] == '') { - if ($hostname != '') { - $message .= '

' . __esc('Cacti Syslog Threshold Alert \'%s\' for Host \'%s\'', $alert['name'], $hostname, 'syslog') . '

'; - } else { - $message .= '

' . __esc('Cacti Syslog Threshold Alert \'%s\'', $alert['name'], 'syslog') . '

'; - } - } else { - $message .= '
' . $alert['body'] . '
'; - } - - $message .= ''; - $message .= ' - - - - - - '; - - $message .= ''; - $message .= ''; - $message .= ''; - $message .= ''; - $message .= '
' . __('Alert Name', 'syslog') . '' . __('Severity', 'syslog') . '' . __('Threshold', 'syslog') . '' . __('Count', 'syslog') . '' . __('Match String', 'syslog') . '
' . html_escape($alert['name']) . '' . $severities[$alert['severity']] . '' . $alert['num'] . '' . sizeof($at) . '' . html_escape($alert['message']) . '

'; - } else { - if ($alert['body'] == '') { - if ($hostname != '') { - $message .= '

' . __esc('Cacti Syslog Alert \'%s\' for Host \'%s\'', $alert['name'], $hostname, 'syslog') . '

'; - } else { - $message .= '

' . __esc('Cacti Syslog Alert \'%s\'', $alert['name'], 'syslog') . '

'; - } - } else { - $message .= '
' . $alert['body'] . '
'; - } - } - - $message .= ''; - $message .= ' - - - - - - '; - } else { - if ($alert['method'] == '1') { - if ($alert['body'] == '') { - $message .= '---------------------------------------------------------------------' . PHP_EOL . PHP_EOL; - if ($hostname != '') { - $message .= __('WARNING: A Syslog Threshold Alert has Been Triggered for Host \'%s\'', $hostname, 'syslog') . PHP_EOL . PHP_EOL; - } else { - $message .= __('WARNING: A Syslog Threshold Alert has Been Triggered', 'syslog') . PHP_EOL . PHP_EOL; - } - } else { - $message .= '---------------------------------------------------------------------' . PHP_EOL . PHP_EOL; - $message .= $alert['body'] . PHP_EOL; - } - - $message .= __('Name:', 'syslog') . ' ' . html_escape($alert['name']) . PHP_EOL; - $message .= __('Severity:', 'syslog') . ' ' . $severities[$alert['severity']] . PHP_EOL; - $message .= __('Threshold:', 'syslog') . ' ' . $alert['num'] . PHP_EOL; - $message .= __('Count:', 'syslog') . ' ' . sizeof($at) . PHP_EOL; - $message .= __('Message String:', 'syslog') . ' ' . html_escape($alert['message']) . PHP_EOL; - } else { - if ($alert['body'] == '') { - if ($hostname != '') { - $message .= __esc('Cacti Syslog Alert \'%s\' for Host \'%s\'', $alert['name'], $hostname, 'syslog'); - } else { - $message .= __esc('Cacti Syslog Alert \'%s\'', $alert['name'], 'syslog'); - } - } else { - $message .= '---------------------------------------------------------------------' . PHP_EOL . PHP_EOL; - $message .= $alert['body']; + /* send the alert */ + syslog_send_alert($alert, $result); } } } - - $hmessage = $message; - $plogged = false; - $flogged = false; - - foreach($at as $a) { - $hostlist[] = $a['host']; - $results['message'] = (isset($results['message']) ? $results['message'] . ', ':'') . $a['message']; - - if (isset($results['priority_id']) && $results['priority_id'] != $a['priority_id'] && !$plogged) { - cacti_log(sprintf('Alert \'%s\' has more than one priority id, last one experienced will be leveraged', $alert['name']), false, 'SYSLOG'); - $plogged = true; - } - - if (isset($results['facility_id']) && $results['facility_id'] != $a['facility_id'] && !$flogged) { - cacti_log(sprintf('Alert \'%s\' has more than one facility id, last one experienced will be leveraged', $alert['name']), false, 'SYSLOG'); - $flogged = true; - } - - $results['priority_id'] = $a['priority_id']; - $results['facility_id'] = $a['facility_id']; - - if (($alert['method'] == 1 && $alert_count < $max_alerts) || $alert['method'] == 0) { - if ($alert['method'] == 0) { - $message = $hmessage; - } - - if ($html) { - $message .= ' - - - - - - '; - } else { - $message .= '---------------------------------------------------------------------' . PHP_EOL . PHP_EOL; - $message .= __('Hostname:', 'syslog') . ' ' . html_escape($a['host']) . PHP_EOL; - $message .= __('Date:', 'syslog') . ' ' . $a['logtime'] . PHP_EOL; - $message .= __('Severity:', 'syslog') . ' ' . $severities[$alert['severity']] . PHP_EOL . PHP_EOL; - $message .= __('Level:', 'syslog') . ' ' . $syslog_levels[$a['priority_id']] . PHP_EOL . PHP_EOL; - $message .= __('Message:', 'syslog') . ' ' . PHP_EOL . $a['message'] . PHP_EOL; - } - } - } - - $hostlist = array_unique($hostlist); - - $syslog_alarms++; - $alert_count++; - - $send = true; - $found = false; - - /** - * If this is a repeat alert type threshold, then check to - * see if it's time to re-alert. - */ - if ($alert['repeat_alert'] > 0) { - if ($hostname != '') { - $found = syslog_db_fetch_cell_prepared('SELECT COUNT(*) - FROM syslog_logs - WHERE alert_id = ? - AND logtime > ? - AND host = ?', - array($alert['id'], $date, $hostname)); - } else { - $found = syslog_db_fetch_cell_prepared('SELECT COUNT(*) - FROM syslog_logs - WHERE alert_id = ? - AND logtime > ? - AND host = "system"', - array($alert['id'], $date)); - } - } - - if ($found) { - $send = false; - } - - if ($html) { - $message .= '
' . __('Hostname', 'syslog') . '' . __('Date', 'syslog') . '' . __('Severity', 'syslog') . '' . __('Level', 'syslog') . '' . __('Message', 'syslog') . '
' . html_escape($a['host']) . '' . $a['logtime'] . '' . $severities[$alert['severity']] . '' . $syslog_levels[$a['priority_id']] . '' . html_escape($a['message']) . '
'; - } else { - $message .= '---------------------------------------------------------------------' . PHP_EOL . PHP_EOL; - } - - if ($html) { - if ($format_ok) { - if ($report_tag) { - $message = str_replace('', $message, $output); - } else { - $message = $output . $message . ''; - } - } else { - $message = '' . $message . ''; - } - } - - /** - * This is a Traditional syslog alert where all matching messages - * will be reported in the notification. - */ - if ($alert['method'] == '0') { - if ($send) { - $sequence = syslog_log_alert($alert['id'], $alert['name'], $alert['severity'], $a, 1, $message); - - $smsalert = __('Sev:', 'syslog') . $severities[$alert['severity']] . __(', Host:', 'syslog') . $a['host'] . __(', URL:', 'syslog') . read_config_option('base_url', true) . '/plugins/syslog/syslog.php?tab=current&id=' . $sequence; - - /** - * Send the Email notification - */ - if ($alert['email'] != '' || $smsalert != '') { - syslog_sendemail(trim($alert['email']), $from, __esc('Event Alert - %s', $alert['name'], 'syslog'), $message, $smsalert); - } - - alert_setup_environment($alert, $results, $hostlist, $hostname); - - /** - * Open a ticket if this options have been selected. - */ - $command = read_config_option('syslog_ticket_command'); - - if ($command != '') { - $command = trim($command); - } - - if ($alert['open_ticket'] == 'on' && $command != '') { - if (is_executable($command)) { - $command = $command . - ' --alert-name=' . cacti_escapeshellarg(clean_up_name($alert['name'])) . - ' --severity=' . cacti_escapeshellarg($alert['severity']) . - ' --hostlist=' . cacti_escapeshellarg(implode(',',$hostlist)) . - ' --message=' . cacti_escapeshellarg($alert['message']); - - $output = array(); - $return = 0; - - exec($command, $output, $return); - - if ($return != 0) { - cacti_log(sprintf('ERROR: Ticket Command Failed. Alert:%s, Exit:%s, Output:%s', $alert['name'], $return, implode(', ', $output)), false, 'SYSLOG'); - } - } - } - - if (trim($alert['command']) != '' && !$found) { - $command = alert_replace_variables($alert, $results, $hostname); - - $logMessage = "SYSLOG NOTICE: Executing '$command'"; - - $cparts = explode(' ', $command); - - if (is_executable($cparts[0])) { - exec($command, $output, $returnCode); - } else { - exec('/bin/sh ' . $command, $output, $returnCode); - } - - // Append the return code to the log message without the dot - $logMessage .= " Command return code: $returnCode"; - - // Log the combined message - cacti_log($logMessage, true, 'SYSTEM'); - } - - } - } elseif ($alert['method'] == 1) { - if ($send) { - /** - * Send the Email notification - */ - if ($alert['email'] != '' || $smsalert != '') { - syslog_sendemail(trim($alert['email']), $from, __esc('Event Alert - %s', $alert['name'], 'syslog'), $message, $smsalert); - } - - $sequence = syslog_log_alert($alert['id'], $alert['name'], $alert['severity'], $at[0], sizeof($at), $message, $hostlist); - $smsalert = __('Sev:', 'syslog') . $severities[$alert['severity']] . __(', Count:', 'syslog') . sizeof($at) . __(', URL:', 'syslog') . read_config_option('base_url', true) . '/plugins/syslog/syslog.php?tab=current&id=' . $sequence; - - alert_setup_environment($alert, $results, $hostlist, $hostname); - - $command = read_config_option('syslog_ticket_command'); - - if ($command != '') { - $command = trim($command); - } - - if ($alert['open_ticket'] == 'on' && $command != '') { - if (is_executable($command)) { - $command = $command . - ' --alert-name=' . cacti_escapeshellarg(clean_up_name($alert['name'])) . - ' --severity=' . cacti_escapeshellarg($alert['severity']) . - ' --hostlist=' . cacti_escapeshellarg(implode(',',$hostlist)) . - ' --message=' . cacti_escapeshellarg($alert['message']); - - $output = array(); - $return = 0; - - exec($command, $output, $return); - - if ($return != 0) { - cacti_log(sprintf('ERROR: Command Failed. Alert:%s, Exit:%s, Output:%s', $alert['name'], $return, implode(', ', $output)), false, 'SYSLOG'); - } - } - } - - if (trim($alert['command']) != '' && !$found) { - $command = alert_replace_variables($alert, $results, $hostname); - - $logMessage = "SYSLOG NOTICE: Executing '$command'"; - - $cparts = explode(' ', $command); - - if (is_executable($cparts[0])) { - exec($command, $output, $returnCode); - } else { - exec('/bin/sh ' . $command, $output, $returnCode); - } - - // Append the return code to the log message without the dot - $logMessage .= " Command return code: $returnCode"; - - // Log the combined message - cacti_log($logMessage, true, 'SYSTEM'); - } - } - } - - syslog_debug("Alert Rule '" . $alert['name'] . "' has been triggered"); } } - return $alert_count; + return array('syslog_alerts' => $syslog_alerts, 'syslog_alarms' => $syslog_alarms); } -/** - * syslog_get_alert_sql - Get the SQL and params for the alert to - * checi. - * - * @param (array) The alert attributes to process - * - * @return (array) The SQL and the prepared array for the SQL - */ -function syslog_get_alert_sql(&$alert, $uniqueID) { +function syslog_get_alert_sql(&$alert, $max_seq) { global $syslogdb_default, $syslog_incoming_config; if (defined('SYSLOG_CONFIG')) { @@ -1656,120 +511,105 @@ function syslog_get_alert_sql(&$alert, $uniqueID) { $sql = 'SELECT * FROM `' . $syslogdb_default . '`.`syslog_incoming` WHERE `' . $syslog_incoming_config['facilityField'] . '` = ? - AND `status` = ?'; + AND `status` = 1 + AND `seq` <= ?'; $params[] = $alert['message']; - $params[] = $uniqueID; + $params[] = $max_seq; } elseif ($alert['type'] == 'messageb') { $sql = 'SELECT * FROM `' . $syslogdb_default . '`.`syslog_incoming` WHERE `' . $syslog_incoming_config['textField'] . '` LIKE ? - AND `status` = ?'; + AND `status` = 1 + AND `seq` <= ?'; $params[] = $alert['message'] . '%'; - $params[] = $uniqueID; + $params[] = $max_seq; } elseif ($alert['type'] == 'messagec') { $sql = 'SELECT * FROM `' . $syslogdb_default . '`.`syslog_incoming` WHERE `' . $syslog_incoming_config['textField'] . '` LIKE ? - AND `status` = ?'; + AND `status` = 1 + AND `seq` <= ?'; $params[] = '%' . $alert['message'] . '%'; - $params[] = $uniqueID; + $params[] = $max_seq; } elseif ($alert['type'] == 'messagee') { $sql = 'SELECT * FROM `' . $syslogdb_default . '`.`syslog_incoming` WHERE `' . $syslog_incoming_config['textField'] . '` LIKE ? - AND `status` = ?'; + AND `status` = 1 + AND `seq` <= ?'; $params[] = '%' . $alert['message']; - $params[] = $uniqueID; + $params[] = $max_seq; } elseif ($alert['type'] == 'host') { $sql = 'SELECT * FROM `' . $syslogdb_default . '`.`syslog_incoming` WHERE `' . $syslog_incoming_config['hostField'] . '` = ? - AND `status` = ?' . $uniqueID; + AND `status` = 1 + AND `seq` <= ?'; $params[] = $alert['message']; - $params[] = $uniqueID; + $params[] = $max_seq; } elseif ($alert['type'] == 'program') { $sql = 'SELECT * FROM `' . $syslogdb_default . '`.`syslog_incoming` WHERE `' . $syslog_incoming_config['programField'] . '` = ? - AND `status` = ?' . $uniqueID; + AND `status` = 1 + AND `seq` <= ?'; $params[] = $alert['message']; - $params[] = $uniqueID; + $params[] = $max_seq; } elseif ($alert['type'] == 'sql') { $sql = 'SELECT * FROM `' . $syslogdb_default . '`.`syslog_incoming` WHERE (' . $alert['message'] . ') - AND `status` = ?'; + AND `status` = 1 + AND `seq` <= ?'; - $params[] = $uniqueID; + $params[] = $max_seq; } return array('sql' => $sql, 'params' => $params); } -/** - * syslog_preprocess_incoming_records - Generate a uniqueID to allow moving of - * records to done table and mark incoming records with the uniqueID and - * then if syslog is configured to strip domains, perform that first. - * - * @return (int) Unique id to allow syslog messages that come in randomly to - * be differentiate between messages to process and messages - * to be left till then ext polling cycle. - */ function syslog_preprocess_incoming_records() { global $syslogdb_default; - while (1) { - $uniqueID = rand(1, 127); - - $count = syslog_db_fetch_cell_prepared('SELECT COUNT(*) - FROM `' . $syslogdb_default . '`.`syslog_incoming` - WHERE `status` = ?', - array($uniqueID)); + $max_seq = syslog_db_fetch_cell('SELECT MAX(seq) FROM `' . $syslogdb_default . '`.`syslog_incoming` WHERE status = 0'); - if ($count == 0) { - break; - } - } + if ($max_seq > 0) { + /* flag all records with the status = 1 prior to moving */ + syslog_db_execute_prepared('UPDATE `' . $syslogdb_default . '`.`syslog_incoming` + SET `status` = 1 + WHERE `status` = 0 + AND `seq` <= ?', + array($max_seq)); - /* flag all records with the uniqueID prior to moving */ - syslog_db_execute_prepared('UPDATE `' . $syslogdb_default . '`.`syslog_incoming` - SET `status` = ? - WHERE `status` = 0', - array($uniqueID)); + syslog_debug('Max Sequence ID = ' . $max_seq); + syslog_debug('-------------------------------------------------------------------------------------'); - syslog_debug('Unique ID = ' . $uniqueID); - syslog_debug('-------------------------------------------------------------------------------------'); + $syslog_incoming = syslog_db_fetch_cell_prepared('SELECT COUNT(seq) + FROM `' . $syslogdb_default . '`.`syslog_incoming` + WHERE `status` = 1 + AND `seq` <= ?', + array($max_seq)); - $syslog_incoming = syslog_db_fetch_cell_prepared('SELECT COUNT(seq) - FROM `' . $syslogdb_default . '`.`syslog_incoming` - WHERE `status` = ?', - array($uniqueID)); + syslog_debug(sprintf('Found %5s - New Message(s) to process', $syslog_incoming)); - syslog_debug(sprintf('Found %5s - New Message(s) to process', $syslog_incoming)); + /* strip domains if we have requested to do so */ + syslog_strip_incoming_domains($max_seq); - /* strip domains if we have requested to do so */ - syslog_strip_incoming_domains($uniqueID); + api_plugin_hook('plugin_syslog_before_processing'); - api_plugin_hook('plugin_syslog_before_processing'); + return array('max_seq' => $max_seq, 'incoming' => $syslog_incoming); + } - return array('uniqueID' => $uniqueID, 'incoming' => $syslog_incoming); + return array('max_seq' => 0, 'incoming' => 0); } -/** - * syslog_strip_incoming_domains - If syslog is setup to strip DNS domain name suffixes do that - * prior to processing the records. - * - * @param (string) The uniqueID records to process - * - * @return (void) - */ -function syslog_strip_incoming_domains($uniqueID) { +function syslog_strip_incoming_domains($max_seq) { global $syslogdb_default; $syslog_domains = read_config_option('syslog_domains'); @@ -1778,29 +618,17 @@ function syslog_strip_incoming_domains($uniqueID) { $domains = explode(',', trim($syslog_domains)); foreach($domains as $domain) { - syslog_db_execute('UPDATE `' . $syslogdb_default . "`.`syslog_incoming` - SET host = SUBSTRING_INDEX(host, '.', 1) - WHERE host LIKE '%$domain' - AND `status` = $uniqueID"); + syslog_db_execute_prepared('UPDATE `' . $syslogdb_default . '`.`syslog_incoming` + SET host = SUBSTRING_INDEX(host, \'.\', 1) + WHERE host LIKE ? + AND `status` = 1 + AND `seq` <= ?', + array(\'%\' . $domain, $max_seq)); } } } - - - -/** - * Check if the hostname is in the cacti hosts table - * Some devices only send IP addresses in syslog messages, and may not be in the DNS - * however they may be in the cacti hosts table as monitored devices. - * - * @param (string) The hostname to check - * @param (int) The unique id for syslog_incoming messages to process - * - * @return (bool) True if the host exists in the Cacti database, false otherwise - */ - -function syslog_check_cacti_hosts($host, $uniqueID) { +function syslog_check_cacti_hosts($host, $max_seq) { global $syslogdb_default; if (empty($host)) { @@ -1818,92 +646,85 @@ function syslog_check_cacti_hosts($host, $uniqueID) { syslog_db_execute_prepared('UPDATE `' . $syslogdb_default . '`.`syslog_incoming` SET host = ? WHERE host = ? - AND `status` = ?', - array($cacti_host['description'], $host, $uniqueID)); - + AND `status` = 1 + AND `seq` <= ?', + array($cacti_host['description'], $host, $max_seq)); + return true; } - + return false; } -/** - * syslog_update_reference_tables - There are many values in the syslog plugin - * that for the purposes of reducing the size of the syslog table are normalized - * the columns includes the facility, the priority, and the hostname. - * - * This function will add those new hostnames to the various reference tables - * and assign an id to each of them. This way the syslog table can be optimized - * for size as much as possible. - * - * @param (int) The unique id for syslog_incoming messages to process - * - * @return (void) - */ -function syslog_update_reference_tables($uniqueID) { + +function syslog_update_reference_tables($max_seq) { global $syslogdb_default; syslog_debug('-------------------------------------------------------------------------------------'); syslog_debug('Updating Reference Tables from New Syslog Records'); /* Validate and resolve hostnames - check DNS first, then Cacti, then mark invalid */ - if (read_config_option('syslog_resolve_hostname') == 'on') { - $hosts = syslog_db_fetch_assoc_prepared('SELECT DISTINCT host - FROM `' . $syslogdb_default . '`.`syslog_incoming` - WHERE `status` = ?', - array($uniqueID)); - - foreach($hosts as $host) { - if (!isset($host['host']) || empty($host['host'])) { - continue; - } - - $resolved = false; - - // Check if hostname resolves via DNS (only if DNS is enabled) - if (read_config_option('syslog_no_dns') != 'on') { - if ($host['host'] != gethostbyname($host['host'])) { - // DNS resolved successfully - $resolved = true; - } - } - - // Check if hostname exists in Cacti hosts table (only if not already resolved via DNS) - if (!$resolved) { - $resolved = syslog_check_cacti_hosts($host['host'], $uniqueID); - } - - // If not resolved via DNS or found in Cacti, prefix the hostname - if (!$resolved) { - $unresolved_host = 'unresolved-' . $host['host']; - cacti_log("SYSLOG WARNING: Hostname '" . $host['host'] . "' could not be resolved via DNS or found in Cacti hosts table, marking as '" . $unresolved_host . "'", false, 'SYSLOG'); - syslog_db_execute_prepared('UPDATE `' . $syslogdb_default . "`.`syslog_incoming` - SET host = ? - WHERE host = ? - AND `status` = ?", - array($unresolved_host, $host['host'], $uniqueID)); - } - } - } + if (read_config_option('syslog_resolve_hostname') == 'on') { + $hosts = syslog_db_fetch_assoc_prepared('SELECT DISTINCT host + FROM `' . $syslogdb_default . '`.`syslog_incoming` + WHERE `status` = 1 + AND `seq` <= ?', + array($max_seq)); + + foreach($hosts as $host) { + if (!isset($host['host']) || empty($host['host'])) { + continue; + } + + $resolved = false; + + // Check if hostname resolves via DNS (only if DNS is enabled) + if (read_config_option('syslog_no_dns') != 'on') { + if ($host['host'] != gethostbyname($host['host'])) { + // DNS resolved successfully + $resolved = true; + } + } + + // Check if hostname exists in Cacti hosts table (only if not already resolved via DNS) + if (!$resolved) { + $resolved = syslog_check_cacti_hosts($host['host'], $max_seq); + } + + // If not resolved via DNS or found in Cacti, prefix the hostname + if (!$resolved) { + $unresolved_host = 'unresolved-' . $host['host']; + cacti_log("SYSLOG WARNING: Hostname '" . $host['host'] . "' could not be resolved via DNS or found in Cacti hosts table, marking as '" . $unresolved_host . "'", false, 'SYSLOG'); + syslog_db_execute_prepared('UPDATE `' . $syslogdb_default . "`.`syslog_incoming` + SET host = ? + WHERE host = ? + AND `status` = 1 + AND `seq` <= ?", + array($unresolved_host, $host['host'], $max_seq)); + } + } + } syslog_db_execute_prepared('INSERT INTO `' . $syslogdb_default . '`.`syslog_programs` (program, last_updated) SELECT DISTINCT program, NOW() FROM `' . $syslogdb_default . '`.`syslog_incoming` - WHERE `status` = ? + WHERE `status` = 1 + AND `seq` <= ? ON DUPLICATE KEY UPDATE program=VALUES(program), last_updated=VALUES(last_updated)', - array($uniqueID)); + array($max_seq)); syslog_db_execute_prepared('INSERT INTO `' . $syslogdb_default . '`.`syslog_hosts` (host, last_updated) SELECT DISTINCT host, NOW() AS last_updated FROM `' . $syslogdb_default . '`.`syslog_incoming` - WHERE `status` = ? + WHERE `status` = 1 + AND `seq` <= ? ON DUPLICATE KEY UPDATE host=VALUES(host), last_updated=NOW()', - array($uniqueID)); + array($max_seq)); syslog_db_execute_prepared('INSERT INTO `' . $syslogdb_default . '`.`syslog_host_facilities` (host_id, facility_id) @@ -1912,26 +733,19 @@ function syslog_update_reference_tables($uniqueID) { ( SELECT DISTINCT host, facility_id FROM `' . $syslogdb_default . "`.`syslog_incoming` - WHERE `status` = ? + WHERE `status` = 1 + AND `seq` <= ? ) AS s INNER JOIN `" . $syslogdb_default . '`.`syslog_hosts` AS sh ON s.host = sh.host - ) + ) AS merge ON DUPLICATE KEY UPDATE host_id=VALUES(host_id), last_updated=NOW()', - array($uniqueID)); + array($max_seq)); } -/** - * syslog_update_statistics - Insert new statistics rows into the syslog statistics - * table for post review - * - * @param (int) The unique id for all syslog incoming records to be processed - * - * @return (void) - */ -function syslog_update_statistics($uniqueID) { +function syslog_update_statistics($max_seq) { global $syslogdb_default, $syslog_cnn; if (read_config_option('syslog_statistics') == 'on') { @@ -1944,10 +758,11 @@ function syslog_update_statistics($uniqueID) { ON sh.host=si.host INNER JOIN syslog_programs AS sp ON sp.program=si.program - WHERE `status` = ? + WHERE `status` = 1 + AND `seq` <= ? GROUP BY host_id, priority_id, facility_id, program_id) AS merge GROUP BY host_id, priority_id, facility_id, program_id', - array($uniqueID)); + array($max_seq)); $stats = db_affected_rows($syslog_cnn); @@ -1955,23 +770,12 @@ function syslog_update_statistics($uniqueID) { } } -/** - * syslog_incoming_to_syslog - Move incoming syslog records to the syslog table - * - * Once all Alerts have been processed, we need to move entries first to - * the syslog table, and then after which we can perform various - * removal rules against them. - * - * @param (int) The unique id for rows in the syslog table - * - * @return (int) The number of rows moved to the syslog table - */ -function syslog_incoming_to_syslog($uniqueID) { +function syslog_incoming_to_syslog($max_seq) { global $syslogdb_default, $syslog_cnn; syslog_db_execute_prepared('INSERT INTO `' . $syslogdb_default . '`.`syslog` (logtime, priority_id, facility_id, program_id, host_id, message) - SELECT logtime, priority_id, facility_id, program_id, host_id, message + SELECT logtime, priority_id, facility_id, sp.program_id, sh.host_id, message FROM ( SELECT logtime, priority_id, facility_id, sp.program_id, sh.host_id, message FROM syslog_incoming AS si @@ -1979,9 +783,10 @@ function syslog_incoming_to_syslog($uniqueID) { ON sh.host = si.host INNER JOIN syslog_programs AS sp ON sp.program = si.program - WHERE `status` = ? + WHERE `status` = 1 + AND `seq` <= ? ) AS merge', - array($uniqueID)); + array($max_seq)); $moved = db_affected_rows($syslog_cnn); @@ -1990,7 +795,10 @@ function syslog_incoming_to_syslog($uniqueID) { syslog_debug(sprintf('Moved %5s - Message(s) to the syslog table', $moved)); - syslog_db_execute('DELETE FROM `' . $syslogdb_default . '`.`syslog_incoming` WHERE status=' . $uniqueID); + syslog_db_execute_prepared('DELETE FROM `' . $syslogdb_default . '`.`syslog_incoming` + WHERE `status` = 1 + AND `seq` <= ?', + array($max_seq)); syslog_debug(sprintf('Deleted %5s - Already Processed Message(s) from incoming', db_affected_rows($syslog_cnn))); @@ -2002,6 +810,10 @@ function syslog_incoming_to_syslog($uniqueID) { return array('moved' => $moved, 'stale' => $stale); } + syslog_debug(sprintf('Deleted %5s - Stale Message(s) from incoming', $stale)); + + return array('moved' => $moved, 'stale' => $stale); +} /** * syslog_postprocess_tables - Remove stale records and optimize tables after diff --git a/syslog_process.php b/syslog_process.php index 0ed1543..ad9d0db 100644 --- a/syslog_process.php +++ b/syslog_process.php @@ -151,13 +151,13 @@ syslog_debug('-------------------------------------------------------------------------------------'); /** - * pre-processing includes marking a uniqueID to be used + * pre-processing includes marking a max_seq to be used * in the processesing of alerts and stripping domains * from hostnames in the case that the administrator * chooses to strip them. */ $results = syslog_preprocess_incoming_records(); -$uniqueID = $results['uniqueID']; +$max_seq = $results['max_seq']; $incoming = $results['incoming']; /** @@ -173,7 +173,7 @@ * time and to speed up searching for these various * columns in the database. */ -syslog_update_reference_tables($uniqueID); +syslog_update_reference_tables($max_seq); /** * The statistics process allows the Cacti @@ -181,19 +181,19 @@ * into the syslog table and what message types are flowing * into it. */ -syslog_update_statistics($uniqueID); +syslog_update_statistics($max_seq); /** * remove records that don't need to to be transferred */ -$results = syslog_remove_items('syslog_incoming', $uniqueID); +$results = syslog_remove_items('syslog_incoming', $max_seq); $removed = $results['removed']; $xferred = $results['xferred']; /** * process the syslog rules and generate alerts */ -$results = syslog_process_alerts($uniqueID); +$results = syslog_process_alerts($max_seq); $alerts = $results['syslog_alerts']; $alarms = $results['syslog_alarms']; @@ -209,7 +209,7 @@ * move records from incoming to syslog table and remove * any stale records to to a poller crash */ -$results = syslog_incoming_to_syslog($uniqueID); +$results = syslog_incoming_to_syslog($max_seq); $moved = $results['moved']; $stale = $results['stale']; diff --git a/tests/regression/issue253_alert_sql_placeholder_test.php b/tests/regression/issue253_alert_sql_placeholder_test.php index 1f4f181..57f3901 100644 --- a/tests/regression/issue253_alert_sql_placeholder_test.php +++ b/tests/regression/issue253_alert_sql_placeholder_test.php @@ -40,4 +40,4 @@ function issue253_assert($condition, $message) { issue253_assert(count($progSql['params']) === 2, 'Program alert SQL must pass two prepared parameters.'); issue253_assert($progSql['params'][1] === 66, 'Program alert status param should be the uniqueID.'); -echo "issue253_alert_sql_placeholder_test passed\n"; +print "issue253_alert_sql_placeholder_test passed\n"; diff --git a/tests/regression/issue254_partition_table_locking_test.php b/tests/regression/issue254_partition_table_locking_test.php index dfccddb..2f02021 100644 --- a/tests/regression/issue254_partition_table_locking_test.php +++ b/tests/regression/issue254_partition_table_locking_test.php @@ -186,4 +186,4 @@ exit(1); } -echo "issue254_partition_table_locking_test passed\n"; +print "issue254_partition_table_locking_test passed\n"; diff --git a/tests/regression/issue258_replication_create_sql_test.php b/tests/regression/issue258_replication_create_sql_test.php index 36e810b..3fbfc4f 100644 --- a/tests/regression/issue258_replication_create_sql_test.php +++ b/tests/regression/issue258_replication_create_sql_test.php @@ -118,4 +118,4 @@ function cacti_log($message, $output = false, $facility = 'SYSTEM', $level = '') exit(1); } -echo "issue258_replication_create_sql_test passed\n"; +print "issue258_replication_create_sql_test passed\n"; diff --git a/tests/regression/issue270_mariadb_detection_strict_test.php b/tests/regression/issue270_mariadb_detection_strict_test.php index 85112b6..a876120 100644 --- a/tests/regression/issue270_mariadb_detection_strict_test.php +++ b/tests/regression/issue270_mariadb_detection_strict_test.php @@ -20,4 +20,4 @@ exit(1); } -echo "issue270_mariadb_detection_strict_test passed\n"; +print "issue270_mariadb_detection_strict_test passed\n"; diff --git a/tests/regression/issue276_bulk_action_dispatch_helper_test.php b/tests/regression/issue276_bulk_action_dispatch_helper_test.php index cec8251..c30560c 100644 --- a/tests/regression/issue276_bulk_action_dispatch_helper_test.php +++ b/tests/regression/issue276_bulk_action_dispatch_helper_test.php @@ -33,4 +33,4 @@ } } -echo "issue276_bulk_action_dispatch_helper_test passed\n"; +print "issue276_bulk_action_dispatch_helper_test passed\n"; diff --git a/tests/regression/issue277_import_payload_loader_test.php b/tests/regression/issue277_import_payload_loader_test.php index d36b4a9..615653f 100644 --- a/tests/regression/issue277_import_payload_loader_test.php +++ b/tests/regression/issue277_import_payload_loader_test.php @@ -65,4 +65,4 @@ } } -echo "issue277_import_payload_loader_test passed\n"; +print "issue277_import_payload_loader_test passed\n"; diff --git a/tests/regression/issue278_command_execution_refactor_test.php b/tests/regression/issue278_command_execution_refactor_test.php index 9ee8167..6583330 100644 --- a/tests/regression/issue278_command_execution_refactor_test.php +++ b/tests/regression/issue278_command_execution_refactor_test.php @@ -115,4 +115,4 @@ } } -echo "issue278_command_execution_refactor_test passed\n"; +print "issue278_command_execution_refactor_test passed\n"; From c1a46f642fbeec6c001796afd933e681c3d6ba74 Mon Sep 17 00:00:00 2001 From: Thomas Vincent Date: Tue, 17 Mar 2026 15:37:53 -0700 Subject: [PATCH 2/2] Refactor: remove legacy uniqueID logic in favor of sequence-based processing (final stability fixes) --- functions.php | 1601 ++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 1380 insertions(+), 221 deletions(-) diff --git a/functions.php b/functions.php index 2abbe37..777959c 100644 --- a/functions.php +++ b/functions.php @@ -22,6 +22,24 @@ +-------------------------------------------------------------------------+ */ +function syslog_apply_selected_items_action($selected_items, $drp_action, $action_map, $export_action = '', $export_items = '') { + if ($selected_items != false) { + if (isset($action_map[$drp_action])) { + $action_function = $action_map[$drp_action]; + + if (function_exists($action_function)) { + foreach($selected_items as $selected_item) { + $action_function($selected_item); + } + } else { + cacti_log("SYSLOG ERROR: Bulk action function '$action_function' not found.", false, 'SYSTEM'); + } + } elseif ($export_action != '' && $drp_action == $export_action) { + $_SESSION['exporter'] = rawurlencode(serialize($selected_items)); + } + } +} + function syslog_include_js() { global $config; ?> @@ -126,55 +144,137 @@ function syslog_sendemail($to, $from, $subject, $message, $smsmessage = '') { } } +function syslog_get_import_xml_payload($redirect_url) { + if (trim(get_nfilter_request_var('import_text')) != '') { + /* textbox input */ + return get_nfilter_request_var('import_text'); + } -function syslog_apply_selected_items_action($selected_items, $drp_action, $action_map, $export_action = '', $export_items = '') { - if ($selected_items != false) { - if (isset($action_map[$drp_action])) { - $action_function = $action_map[$drp_action]; + if (isset($_FILES['import_file']['tmp_name']) && + $_FILES['import_file']['tmp_name'] != 'none' && + $_FILES['import_file']['tmp_name'] != '') { + /* file upload */ + $tmp_name = $_FILES['import_file']['tmp_name']; - if (function_exists($action_function)) { - foreach($selected_items as $selected_item) { - $action_function($selected_item); - } - } else { - cacti_log("SYSLOG ERROR: Bulk action function '$action_function' not found.", false, 'SYSTEM'); - } - } elseif ($export_action != '' && $drp_action == $export_action) { - /* Re-serialize the sanitized array and URL-encode so the value is - * safe to embed in a JS document.location string (avoids injection - * via the raw request value that $export_items carries). */ - $_SESSION['exporter'] = rawurlencode(serialize($selected_items)); + if (!isset($_FILES['import_file']['error']) || $_FILES['import_file']['error'] !== UPLOAD_ERR_OK) { + header('Location: ' . $redirect_url); + exit; } + + if (!is_uploaded_file($tmp_name)) { + header('Location: ' . $redirect_url); + exit; + } + + $fp = fopen($tmp_name, 'rb'); + + if ($fp === false) { + cacti_log('SYSLOG ERROR: Failed to open uploaded import file', false, 'SYSTEM'); + header('Location: ' . $redirect_url); + exit; + } + + $xml_data = fread($fp, filesize($tmp_name)); + fclose($fp); + + if ($xml_data === false) { + cacti_log('SYSLOG ERROR: Failed to read uploaded import file', false, 'SYSTEM'); + header('Location: ' . $redirect_url); + exit; + } + + return $xml_data; } + + header('Location: ' . $redirect_url); + exit; } function syslog_is_partitioned() { - global $syslogdb_default, $database_default; + global $syslogdb_default; - if (defined('SYSLOG_CONFIG')) { - include(SYSLOG_CONFIG); + /* see if the table is partitioned */ + $syntax = syslog_db_fetch_row("SHOW CREATE TABLE `" . $syslogdb_default . "`.`syslog`"); + if (substr_count($syntax['Create Table'], 'PARTITION')) { + return true; + } else { + return false; } +} - $syslog_partitioning = read_config_option('syslog_partitioning'); +/** + * This function will manage old data for non-partitioned tables + */ +function syslog_traditional_manage() { + global $syslogdb_default, $syslog_cnn; - if ($syslog_partitioning == 'on') { - $table_name = 'syslog'; + /* determine the oldest date to retain */ + if (read_config_option('syslog_retention') > 0) { + $retention = date('Y-m-d', time() - (86400 * read_config_option('syslog_retention'))); + } else { + $retention = date('Y-m-d', time() - (30 * 86400)); + set_config_option('syslog_retention', '30'); + } - $result = syslog_db_fetch_cell_prepared('SELECT COUNT(*) - FROM information_schema.partitions - WHERE table_schema = ? - AND table_name = ? - AND partition_name IS NOT NULL', - array($syslogdb_default, $table_name)); + /* delete from the main syslog table first */ + syslog_db_execute("DELETE FROM `" . $syslogdb_default . "`.`syslog` WHERE logtime < '$retention'"); - if ($result > 0) { - return true; - } + $syslog_deleted = db_affected_rows($syslog_cnn); + + /* now delete from the syslog removed table */ + syslog_db_execute("DELETE FROM `" . $syslogdb_default . "`.`syslog_removed` WHERE logtime < '$retention'"); + + $syslog_deleted += db_affected_rows($syslog_cnn); + + syslog_debug(sprintf('Deleted %5s, Syslog Message(s) (older than %s)', $syslog_deleted, $retention)); + + return $syslog_deleted; +} + +/** + * This function will manage a partitioned table by checking for time to create + */ +function syslog_partition_manage() { + $syslog_deleted = 0; + + if (syslog_partition_check('syslog')) { + syslog_partition_create('syslog'); + $syslog_deleted = syslog_partition_remove('syslog'); } - return false; + if (syslog_partition_check('syslog_removed')) { + syslog_partition_create('syslog_removed'); + $syslog_deleted += syslog_partition_remove('syslog_removed'); + } + + return $syslog_deleted; +} + +/** + * Validate tables that support partition maintenance. + * + * Any value added to the allowlist MUST match ^[a-z_]+$ so it is safe + * for identifier interpolation in DDL statements (MySQL does not support + * parameter binding for identifiers). + */ +function syslog_partition_table_allowed($table) { + if (!in_array($table, array('syslog', 'syslog_removed'), true)) { + return false; + } + + /* Defense-in-depth: reject values unsafe for identifier interpolation. */ + if (!preg_match('/^[a-z_]+$/', $table)) { + return false; + } + + return true; } +/** + * Create a new partition for the specified table. + * + * @return bool true on success, false on lock failure or disallowed table. + */ function syslog_partition_create($table) { global $syslogdb_default; @@ -182,22 +282,57 @@ function syslog_partition_create($table) { return false; } - $lock_name = 'syslog_partition_create_' . $table; - $lock = syslog_db_fetch_cell_prepared('SELECT GET_LOCK(?, 10)', array($lock_name)); + /* Hash to guarantee the lock name stays within MySQL's 64-byte limit. */ + $lock_name = substr(hash('sha256', $syslogdb_default . '.syslog_partition_create.' . $table), 0, 60); + + /* + * 10-second timeout is sufficient: partition maintenance runs once per + * poller cycle (typically 5 minutes), so sustained contention is not + * expected. A failure is logged so monitoring can detect repeated misses. + */ + $locked = syslog_db_fetch_cell_prepared('SELECT GET_LOCK(?, 10)', array($lock_name)); + + if ($locked === null) { + /* NULL means the GET_LOCK call itself failed, not just contention. */ + cacti_log("SYSLOG: GET_LOCK call failed for partition create on '$table'", false, 'SYSTEM'); + return false; + } - if (!$lock) { - syslog_debug("SYSLOG: Failed to acquire lock for $lock_name"); + if ((int)$locked !== 1) { + cacti_log("SYSLOG: Unable to acquire partition create lock for '$table'", false, 'SYSTEM'); return false; } try { - $next_date = date('Ymd', strtotime('+1 day')); - $part_name = 'd' . $next_date; + /* determine the format of the table name */ + $time = time(); + $cformat = 'd' . date('Ymd', $time); + $lnow = date('Y-m-d', $time+86400); - syslog_db_execute("ALTER TABLE `" . $syslogdb_default . "`.`$table` ADD PARTITION (PARTITION $part_name VALUES LESS THAN ($next_date))"); - cacti_log("SYSLOG: Created new partition '$part_name' for table '$table'", false, 'SYSTEM'); - } catch (Exception $e) { - cacti_log("SYSLOG ERROR: Failed to create partition for $table: " . $e->getMessage(), false, 'SYSTEM'); + $exists = syslog_db_fetch_row_prepared("SELECT * + FROM `information_schema`.`partitions` + WHERE table_schema = ? + AND partition_name = ? + AND table_name = ? + ORDER BY partition_ordinal_position", + array($syslogdb_default, $cformat, $table)); + + if (!cacti_sizeof($exists)) { + cacti_log("SYSLOG: Creating new partition '$cformat'", false, 'SYSTEM'); + + syslog_debug("Creating new partition '$cformat'"); + + /* + * MySQL does not support parameter binding for DDL identifiers + * or partition definitions. $table is safe because it passed + * syslog_partition_table_allowed() (two-value allowlist plus + * regex guard). $cformat and $lnow derive from date() and + * contain only digits, hyphens, and the letter 'd'. + */ + syslog_db_execute("ALTER TABLE `" . $syslogdb_default . "`.`$table` REORGANIZE PARTITION dMaxValue INTO ( + PARTITION $cformat VALUES LESS THAN (TO_DAYS('$lnow')), + PARTITION dMaxValue VALUES LESS THAN MAXVALUE)"); + } } finally { syslog_db_fetch_cell_prepared('SELECT RELEASE_LOCK(?)', array($lock_name)); } @@ -205,57 +340,77 @@ function syslog_partition_create($table) { return true; } +/** + * Remove old partitions for the specified table. + */ function syslog_partition_remove($table) { global $syslogdb_default; if (!syslog_partition_table_allowed($table)) { - return false; + cacti_log("SYSLOG: partition_remove called with disallowed table '$table'", false, 'SYSTEM'); + return 0; } - $syslog_deleted = 0; - $lock_name = 'syslog_partition_remove_' . $table; - $lock = syslog_db_fetch_cell_prepared('SELECT GET_LOCK(?, 10)', array($lock_name)); + $lock_name = substr(hash('sha256', $syslogdb_default . '.syslog_partition_remove.' . $table), 0, 60); - if (!$lock) { - syslog_debug("SYSLOG: Failed to acquire lock for $lock_name"); - return false; + $locked = syslog_db_fetch_cell_prepared('SELECT GET_LOCK(?, 10)', array($lock_name)); + + if ($locked === null) { + cacti_log("SYSLOG: GET_LOCK call failed for partition remove on '$table'", false, 'SYSTEM'); + return 0; } - $number_of_partitions = syslog_db_fetch_assoc_prepared("SELECT PARTITION_NAME - FROM `information_schema`.`partitions` - WHERE table_schema = ? AND table_name = ? - AND PARTITION_NAME IS NOT NULL - ORDER BY partition_ordinal_position", - array($syslogdb_default, $table)); + if ((int)$locked !== 1) { + cacti_log("SYSLOG: Unable to acquire partition remove lock for '$table'", false, 'SYSTEM'); + return 0; + } + + $syslog_deleted = 0; + + try { + $number_of_partitions = syslog_db_fetch_assoc_prepared("SELECT * + FROM `information_schema`.`partitions` + WHERE table_schema = ? AND table_name = ? + ORDER BY partition_ordinal_position", + array($syslogdb_default, $table)); + + $days = read_config_option('syslog_retention'); - $days = read_config_option('syslog_retention'); + syslog_debug("There are currently '" . sizeof($number_of_partitions) . "' Syslog Partitions, We will keep '$days' of them."); - syslog_debug("There are currently '" . cacti_sizeof($number_of_partitions) . "' Syslog Partitions, We will keep '$days' of them."); + if ($days > 0) { + $user_partitions = sizeof($number_of_partitions) - 1; + if ($user_partitions >= $days) { + $i = 0; + while ($user_partitions > $days) { + $oldest = $number_of_partitions[$i]; - if ($days > 0) { - $user_partitions = cacti_sizeof($number_of_partitions); - if ($user_partitions > $days) { - $i = 0; - while ($user_partitions > $days) { - $oldest = $number_of_partitions[$i]; + cacti_log("SYSLOG: Removing old partition '" . $oldest['PARTITION_NAME'] . "'", false, 'SYSTEM'); - cacti_log("SYSLOG: Removing old partition '" . $oldest['PARTITION_NAME'] . "'", false, 'SYSTEM'); - syslog_debug("Removing partition '" . $oldest['PARTITION_NAME'] . "'"); + syslog_debug("Removing partition '" . $oldest['PARTITION_NAME'] . "'"); - syslog_db_execute("ALTER TABLE `" . $syslogdb_default . "`.`$table` DROP PARTITION " . $oldest['PARTITION_NAME']); + syslog_db_execute("ALTER TABLE `" . $syslogdb_default . "`.`$table` DROP PARTITION " . $oldest['PARTITION_NAME']); - $i++; - $user_partitions--; - $syslog_deleted++; + $i++; + $user_partitions--; + $syslog_deleted++; + } } } + } finally { + syslog_db_fetch_cell_prepared('SELECT RELEASE_LOCK(?)', array($lock_name)); } - syslog_db_fetch_cell_prepared('SELECT RELEASE_LOCK(?)', array($lock_name)); - return $syslog_deleted; } +/* + * syslog_partition_check is a read-only SELECT against information_schema. + * It does not execute DDL, so it does not need the named lock that + * syslog_partition_create and syslog_partition_remove acquire. External + * serialization is provided by the poller cycle calling + * syslog_partition_manage(). + */ function syslog_partition_check($table) { global $syslogdb_default; @@ -271,31 +426,26 @@ function syslog_partition_check($table) { $last_part = syslog_db_fetch_cell_prepared("SELECT PARTITION_NAME FROM `information_schema`.`partitions` WHERE table_schema = ? AND table_name = ? - AND PARTITION_NAME IS NOT NULL ORDER BY partition_ordinal_position DESC - LIMIT 1", + LIMIT 1,1", array($syslogdb_default, $table)); - if (!$last_part) { - return true; - } + $lformat = str_replace('d', '', $last_part); + $cformat = date('Ymd'); - $lformat = str_replace('d', '', $last_part); - $cformat = date('Ymd'); - - if ($lformat <= $cformat) { + if ($cformat > $lformat) { return true; } else { return false; } } -function syslog_partition_table_allowed($table) { - $allowed = array('syslog', 'syslog_incoming'); - if (in_array($table, $allowed)) { - return true; +function syslog_check_changed($request, $session) { + if ((isset_request_var($request)) && (isset($_SESSION[$session]))) { + if (get_request_var($request) != $_SESSION[$session]) { + return 1; + } } - return false; } function syslog_remove_items($table, $max_seq) { @@ -306,10 +456,9 @@ function syslog_remove_items($table, $max_seq) { syslog_debug('Processing Removal Rules...'); if ($table == 'syslog') { - $rows = syslog_db_fetch_assoc_prepared("SELECT * + $rows = syslog_db_fetch_assoc("SELECT * FROM `" . $syslogdb_default . "`.`syslog_remove` - WHERE enabled = 'on' - AND id <= ?", array($max_seq)); + WHERE enabled = 'on'"); } else { $rows = syslog_db_fetch_assoc('SELECT * FROM `' . $syslogdb_default . '`.`syslog_remove` @@ -333,104 +482,701 @@ function syslog_remove_items($table, $max_seq) { if (cacti_sizeof($rows)) { foreach($rows as $remove) { - $sql = ''; - $sql1 = ''; - $sql2 = ''; - - $params = array(); + $sql_where = ''; + $params = array(); if ($remove['type'] == 'facility') { - $sql = 'FROM `' . $syslogdb_default . '`.`syslog_incoming` - WHERE `' . $syslog_incoming_config['facilityField'] . '` = ? - AND `status` = 1 - AND `seq` <= ?'; + if ($table == 'syslog_incoming') { + $sql_where = 'WHERE `' . $syslog_incoming_config['facilityField'] . '` = ? + AND `status` = 1 + AND `seq` <= ?'; + + $params[] = $remove['message']; + $params[] = $max_seq; + } else { + $facility_id = syslog_db_fetch_cell_prepared('SELECT facility_id + FROM `' . $syslogdb_default . '`.`syslog_facilities` + WHERE facility = ?', array($remove['message'])); + + if (!empty($facility_id)) { + $sql_where = 'WHERE facility_id = ?'; + $params[] = $facility_id; + } + } + } else if ($remove['type'] == 'program') { + if ($table == 'syslog_incoming') { + $sql_where = 'WHERE `program` = ? + AND `status` = 1 + AND `seq` <= ?'; + + $params[] = $remove['message']; + $params[] = $max_seq; + } else { + $program_id = syslog_db_fetch_cell_prepared('SELECT program_id + FROM `' . $syslogdb_default . '`.`syslog_programs` + WHERE program = ?', array($remove['message'])); + + if (!empty($program_id)) { + $sql_where = 'WHERE program_id = ?'; + $params[] = $program_id; + } + } + } elseif ($remove['type'] == 'host') { + if ($table == 'syslog_incoming') { + $sql_where = 'WHERE `host` = ? + AND `status` = 1 + AND `seq` <= ?'; + + $params[] = $remove['message']; + $params[] = $max_seq; + } else { + $host_id = syslog_db_fetch_cell_prepared('SELECT host_id + FROM `' . $syslogdb_default . '`.`syslog_hosts` + WHERE host = ?', array($remove['message'])); - $params[] = $remove['message']; - $params[] = $max_seq; + if (!empty($host_id)) { + $sql_where = 'WHERE host_id = ?'; + $params[] = $host_id; + } + } } elseif ($remove['type'] == 'messageb') { - $sql = 'FROM `' . $syslogdb_default . '`.`syslog_incoming` - WHERE `' . $syslog_incoming_config['textField'] . '` LIKE ? - AND `status` = 1 - AND `seq` <= ?'; + if ($table == 'syslog_incoming') { + $sql_where = 'WHERE `' . $syslog_incoming_config['textField'] . '` LIKE ? + AND `status` = 1 + AND `seq` <= ?'; - $params[] = $remove['message'] . '%'; - $params[] = $max_seq; + $params[] = $remove['message'] . '%'; + $params[] = $max_seq; + } else { + $sql_where = 'WHERE message LIKE ?'; + $params[] = $remove['message'] . '%'; + } } elseif ($remove['type'] == 'messagec') { - $sql = 'FROM `' . $syslogdb_default . '`.`syslog_incoming` - WHERE `' . $syslog_incoming_config['textField'] . '` LIKE ? - AND `status` = 1 - AND `seq` <= ?'; + if ($table == 'syslog_incoming') { + $sql_where = 'WHERE `' . $syslog_incoming_config['textField'] . '` LIKE ? + AND `status` = 1 + AND `seq` <= ?'; - $params[] = '%' . $remove['message'] . '%'; - $params[] = $max_seq; + $params[] = '%' . $remove['message'] . '%'; + $params[] = $max_seq; + } else { + $sql_where = 'WHERE message LIKE ?'; + $params[] = '%' . $remove['message'] . '%'; + } } elseif ($remove['type'] == 'messagee') { - $sql = 'FROM `' . $syslogdb_default . '`.`syslog_incoming` - WHERE `' . $syslog_incoming_config['textField'] . '` LIKE ? - AND `status` = 1 - AND `seq` <= ?'; + if ($table == 'syslog_incoming') { + $sql_where = 'WHERE `' . $syslog_incoming_config['textField'] . '` LIKE ? + AND `status` = 1 + AND `seq` <= ?'; - $params[] = '%' . $remove['message']; - $params[] = $max_seq; - } elseif ($remove['type'] == 'host') { - $sql = 'FROM `' . $syslogdb_default . '`.`syslog_incoming` - WHERE `' . $syslog_incoming_config['hostField'] . '` = ? - AND `status` = 1 - AND `seq` <= ?'; - - $params[] = $remove['message']; - $params[] = $max_seq; - } elseif ($remove['type'] == 'program') { - $sql = 'FROM `' . $syslogdb_default . '`.`syslog_incoming` - WHERE `' . $syslog_incoming_config['programField'] . '` = ? - AND `status` = 1 - AND `seq` <= ?'; - - $params[] = $remove['message']; - $params[] = $max_seq; + $params[] = '%' . $remove['message']; + $params[] = $max_seq; + } else { + $sql_where = 'WHERE message LIKE ?'; + $params[] = '%' . $remove['message']; + } } elseif ($remove['type'] == 'sql') { - $sql = 'FROM `' . $syslogdb_default . '`.`syslog_incoming` - WHERE (' . $remove['message'] . ') - AND `status` = 1 - AND `seq` <= ?'; + if ($table == 'syslog_incoming') { + $sql_where = 'WHERE (' . $remove['message'] . ') + AND `status` = 1 + AND `seq` <= ?'; - $params[] = $max_seq; + $params[] = $max_seq; + } else { + $sql_where = 'WHERE (' . $remove['message'] . ')'; + } } - if ($sql != '') { - if ($remove['action'] == 1) { - /* remove them */ - $sql1 = 'DELETE ' . $sql; - syslog_db_execute_prepared($sql1, $params); - $removed += db_affected_rows($syslog_cnn); - } elseif ($remove['action'] == 2) { - /* transfer them */ - $sql1 = 'INSERT INTO `' . $syslogdb_default . '`.`syslog` - (logtime, priority_id, facility_id, program_id, host_id, message) - SELECT logtime, priority_id, facility_id, sp.program_id, sh.host_id, message - FROM ( - SELECT logtime, priority_id, facility_id, sp.program_id, sh.host_id, message ' . $sql . ' - INNER JOIN syslog_hosts AS sh - ON sh.host = syslog_incoming.host - INNER JOIN syslog_programs AS sp - ON sp.program = syslog_incoming.program - ) AS merge'; - - syslog_db_execute_prepared($sql1, $params); + if ($sql_where != '') { + if ($remove['method'] != 'del') { + if ($table == 'syslog_incoming') { + syslog_db_execute_prepared('INSERT INTO `' . $syslogdb_default . '`.`syslog_removed` + (logtime, priority_id, facility_id, program_id, host_id, message) + SELECT si.logtime, si.priority_id, si.facility_id, sp.program_id, sh.host_id, si.message + FROM `' . $syslogdb_default . '`.`syslog_incoming` AS si + INNER JOIN `' . $syslogdb_default . '`.`syslog_hosts` AS sh + ON sh.host = si.host + INNER JOIN `' . $syslogdb_default . '`.`syslog_programs` AS sp + ON sp.program = si.program ' . $sql_where, $params); + } else { + syslog_db_execute_prepared('INSERT INTO `' . $syslogdb_default . '`.`syslog_removed` + (logtime, priority_id, facility_id, program_id, host_id, message) + SELECT logtime, priority_id, facility_id, program_id, host_id, message + FROM `' . $syslogdb_default . '`.`syslog` ' . $sql_where, $params); + } + $xferred += db_affected_rows($syslog_cnn); + } + + if ($table == 'syslog_incoming') { + syslog_db_execute_prepared('DELETE FROM `' . $syslogdb_default . '`.`syslog_incoming` ' . $sql_where, $params); + } else { + syslog_db_execute_prepared('DELETE FROM `' . $syslogdb_default . '`.`syslog` ' . $sql_where, $params); + } + + $removed += db_affected_rows($syslog_cnn); + } + } + } + + syslog_debug(sprintf('Removed %5s - Record(s) from ' . $table, $removed)); + syslog_debug(sprintf('Xferred %5s - Record(s) to the syslog_removed table', $xferred)); + + return array('removed' => $removed, 'xferred' => $xferred); +} + +/** function syslog_log_row_color() + * This function set's the CSS for each row of the syslog table as it is displayed + * it supports both the legacy as well as the new approach to controlling these + * colors. +*/ +function syslog_log_row_color($severity, $tip_title) { + switch($severity) { + case '': + case '0': + $class = 'logInfo'; + break; + case '1': + $class = 'logWarning'; + break; + case '2': + $class = 'logAlert'; + break; + } + + print "\n"; +} + +/** function syslog_row_color() + * This function set's the CSS for each row of the syslog table as it is displayed + * it supports both the legacy as well as the new approach to controlling these + * colors. +*/ +function syslog_row_color($priority, $message) { + switch($priority) { + case '0': + $class = 'logEmergency'; + break; + case '1': + $class = 'logAlert'; + break; + case '2': + $class = 'logCritical'; + break; + case '3': + $class = 'logError'; + break; + case '4': + $class = 'logWarning'; + break; + case '5': + $class = 'logNotice'; + break; + case '6': + $class = 'logInfo'; + break; + case '7': + $class = 'logDebug'; + break; + } + + print ""; + return $class; +} + +function sql_hosts_where($tab) { + global $hostfilter, $hostfilter_log, $syslog_incoming_config; + global $syslogdb_default; + + $hostfilter = ''; + $hostfilter_log = ''; + $hosts_array = array(); + + if (!isempty_request_var('host') && get_nfilter_request_var('host') != 'null') { + $hostarray = explode(',', trim(get_nfilter_request_var('host'))); + if ($hostarray[0] != '0') { + foreach($hostarray as $host_id) { + input_validate_input_number($host_id); + + if ($host_id > 0) { + $log_host = syslog_db_fetch_cell_prepared('SELECT host + FROM `' . $syslogdb_default . '`.`syslog_hosts` + WHERE host_id = ?', + array($host_id)); + + if (!empty($log_host)) { + $hosts_array[] = db_qstr($log_host); + } + } + } + + if (cacti_sizeof($hosts_array)) { + $hostfilter_log = ' host IN(' . implode(',', $hosts_array) . ')'; + } + + $hostfilter .= ($hostfilter != '' ? ' AND ':'') . ' host_id IN(' . implode(',', $hostarray) . ')'; + } + } +} + +function syslog_export($tab) { + global $syslog_incoming_config, $severities; + global $syslogdb_default; + + if (defined('SYSLOG_CONFIG')) { + include(SYSLOG_CONFIG); + } + + if ($tab == 'syslog') { + header('Content-type: application/excel'); + header('Content-Disposition: attachment; filename=syslog_view-' . date('Y-m-d',time()) . '.csv'); + + $sql_where = ''; + $messages = get_syslog_messages($sql_where, 100000, $tab); + + $hosts = array_rekey( + syslog_db_fetch_assoc('SELECT host_id, host + FROM `' . $syslogdb_default . '`.`syslog_hosts`'), + 'host_id', 'host' + ); + + $facilities = array_rekey( + syslog_db_fetch_assoc('SELECT facility_id, facility + FROM `' . $syslogdb_default . '`.`syslog_facilities`'), + 'facility_id', 'facility' + ); + + $priorities = array_rekey( + syslog_db_fetch_assoc('SELECT priority_id, priority + FROM `' . $syslogdb_default . '`.`syslog_priorities`'), + 'priority_id', 'priority' + ); + + $programs = array_rekey( + syslog_db_fetch_assoc('SELECT program_id, program + FROM `' . $syslogdb_default . '`.`syslog_programs`'), + 'program_id', 'program' + ); + + print 'host, facility, priority, program, date, message' . "\r\n"; + + if (cacti_sizeof($messages)) { + foreach ($messages as $message) { + if (isset($facilities[$message['facility_id']])) { + $facility = $facilities[$message['facility_id']]; + } else { + $facility = 'Unknown'; + } + + if (isset($programs[$message['program_id']])) { + $program = $programs[$message['program_id']]; + } else { + $program = 'Unknown'; + } + + if (isset($priorities[$message['priority_id']])) { + $priority = $priorities[$message['priority_id']]; + } else { + $priority = 'Unknown'; + } - $sql2 = 'DELETE ' . $sql; - syslog_db_execute_prepared($sql2, $params); + if (isset($hosts[$message['host_id']])) { + $host = $hosts[$message['host_id']]; + } else { + $host = 'Unknown'; + } + + print + '"' . + $host . '","' . + ucfirst($facility) . '","' . + ucfirst($priority) . '","' . + ucfirst($program) . '","' . + $message['logtime'] . '","' . + $message[$syslog_incoming_config['textField']] . '"' . "\r\n"; + } + } + } else { + header('Content-type: application/excel'); + header('Content-Disposition: attachment; filename=alert_log_view-' . date('Y-m-d',time()) . '.csv'); + + $sql_where = ''; + $messages = get_syslog_messages($sql_where, 100000, $tab); + + print 'name, severity, date, message, host, facility, priority, count' . "\r\n"; + + if (cacti_sizeof($messages)) { + foreach ($messages as $message) { + if (isset($severities[$message['severity']])) { + $severity = $severities[$message['severity']]; + } else { + $severity = 'Unknown'; } + + print + '"' . + $message['name'] . '","' . + $severity . '","' . + $message['logtime'] . '","' . + $message['logmsg'] . '","' . + $message['host'] . '","' . + ucfirst($message['facility']) . '","' . + ucfirst($message['priority']) . '","' . + $message['count'] . '"' . "\r\n"; } } } +} + +function syslog_debug($message) { + global $debug; + + if ($debug) { + print date('H:m:s') . ' SYSLOG DEBUG: ' . trim($message) . PHP_EOL; + } +} + +function syslog_log_alert($alert_id, $alert_name, $severity, $msg, $count = 1, $html = '', $hosts = array()) { + global $config, $severities; + global $syslogdb_default; + + if ($count <= 1) { + $save['seq'] = ''; + $save['alert_id'] = $alert_id; + $save['logseq'] = $msg['seq']; + $save['logtime'] = $msg['logtime']; + $save['logmsg'] = $msg['message']; + $save['host'] = $msg['host']; + $save['facility_id'] = $msg['facility_id']; + $save['priority_id'] = $msg['priority_id']; + $save['count'] = 1; + $save['html'] = $html; + + $id = 0; + $id = syslog_sql_save($save, '`' . $syslogdb_default . '`.`syslog_logs`', 'seq'); + + $save['seq'] = $id; + $save['alert_name'] = $alert_name; + api_plugin_hook_function('syslog_update_hostsalarm', $save); + + cacti_log("WARNING: The Syslog Alert '$alert_name' with Severity '" . $severities[$severity] . "', has been Triggered on Host '" . $msg['host'] . "', and Sequence '$id'", false, 'SYSLOG'); + + return $id; + } else { + $save['seq'] = ''; + $save['alert_id'] = $alert_id; + $save['logseq'] = 0; + $save['logtime'] = date('Y-m-d H:i:s'); + $save['logmsg'] = $alert_name; + $save['host'] = 'N/A'; + $save['facility_id'] = $msg['facility_id']; + $save['priority_id'] = $msg['priority_id']; + $save['count'] = $count; + $save['html'] = $html; + + $id = 0; + $id = syslog_sql_save($save, '`' . $syslogdb_default . '`.`syslog_logs`', 'seq'); + + $save['seq'] = $id; + $save['alert_name'] = $alert_name; + + if (cacti_sizeof($hosts)) { + foreach($hosts as $host) { + $save['host'] = $host; + api_plugin_hook_function('syslog_update_hostsalarm', $save); + } + } + + cacti_log("WARNING: The Syslog Instance Alert '$alert_name' with Severity '" . $severities[$severity] . "', has been Triggered, Count was '" . $count . "', and Sequence '$id'", false, 'SYSLOG'); + + return $id; + } +} + +function syslog_manage_items($from_table, $to_table) { + global $config, $syslog_cnn, $syslog_incoming_config; + global $syslogdb_default; + + /* Select filters to work on */ + $rows = syslog_db_fetch_assoc('SELECT * FROM `' . $syslogdb_default . "`.`syslog_remove` WHERE enabled='on'"); + + syslog_debug(sprintf('Found %5s - Removal Rule(s) to process', cacti_sizeof($rows))); + + $removed = 0; + $xferred = 0; + $total = 0; + + if (cacti_sizeof($rows)) { + foreach($rows as $remove) { + syslog_debug('Processing Rule - ' . $remove['message']); + + $sql_sel = ''; + $sql_dlt = ''; + + if ($remove['type'] == 'facility') { + if ($remove['method'] != 'del') { + $sql_sel = "SELECT seq FROM `" . $syslogdb_default . "`. $from_table + WHERE facility_id IN + (SELECT distinct facility_id FROM `". $syslogdb_default . "`syslog_facilities + WHERE facility ='". $remove['message']."')"; + } else { + $sql_dlt = "DELETE FROM `" . $syslogdb_default . "`. $from_table + WHERE facility_id IN + (SELECT distinct facility_id FROM `". $syslogdb_default . "`syslog_facilities + WHERE facility ='". $remove['message']."')"; + } + + } elseif ($remove['type'] == 'host') { + if ($remove['method'] != 'del') { + $sql_sel = "SELECT seq + FROM `" . $syslogdb_default . "`. $from_table + WHERE host_id in + (SELECT distinct host_id FROM `". $syslogdb_default . "`syslog_hosts + WHERE host ='". $remove['message']."')"; + } else { + $sql_dlt = "DELETE FROM `" . $syslogdb_default . "`. $from_table + WHERE host_id in + (SELECT distinct host_id FROM `". $syslogdb_default . "`syslog_hosts + WHERE host ='". $remove['message']."')"; + } + } elseif ($remove['type'] == 'messageb') { + if ($remove['method'] != 'del') { + $sql_sel = "SELECT seq FROM `" . $syslogdb_default . "`. $from_table + WHERE message LIKE '" . $remove['message'] . "%' "; + } else { + $sql_dlt = "DELETE FROM `" . $syslogdb_default . "`. $from_table + WHERE message LIKE '" . $remove['message'] . "%' "; + } + + } elseif ($remove['type'] == 'messagec') { + if ($remove['method'] != 'del') { + $sql_sel = "SELECT seq FROM `" . $syslogdb_default . "`. $from_table + WHERE message LIKE '%" . $remove['message'] . "%' "; + } else { + $sql_dlt = "DELETE FROM `" . $syslogdb_default . "`. $from_table + WHERE message LIKE '%" . $remove['message'] . "%' "; + } + } elseif ($remove['type'] == 'messagee') { + if ($remove['method'] != 'del') { + $sql_sel = "SELECT seq FROM `" . $syslogdb_default . "`. $from_table + WHERE message LIKE '%" . $remove['message'] . "' "; + } else { + $sql_dlt = "DELETE FROM `" . $syslogdb_default . "`. $from_table + WHERE message LIKE '%" . $remove['message'] . "' "; + } + } elseif ($remove['type'] == 'sql') { + if ($remove['method'] != 'del') { + $sql_sel = "SELECT seq FROM `" . $syslogdb_default . "`. $from_table + WHERE message (" . $remove['message'] . ") "; + } else { + $sql_dlt = "DELETE FROM `" . $syslogdb_default . "`. $from_table + WHERE message (" . $remove['message'] . ") "; + } + } + + if ($sql_sel != '' || $sql_dlt != '') { + $debugm = ''; + /* process the removal rule first */ + if ($sql_sel != '') { + $move_count = 0; + /* first insert, then delete */ + $move_records = syslog_db_fetch_assoc($sql_sel); + syslog_debug(sprintf('Found %5s - Message(s)', cacti_sizeof($move_records))); + + if (cacti_sizeof($move_records)) { + $all_seq = ''; + $messages_moved = 0; + foreach($move_records as $move_record) { + $all_seq = $all_seq . ", " . $move_record['seq']; + } + + $all_seq = preg_replace('/^,/i', '', $all_seq); + syslog_db_execute("INSERT INTO `". $syslogdb_default . "`.`". $to_table ."` + (facility_id, priority_id, host_id, logtime, message) + (SELECT facility_id, priority_id, host_id, logtime, message + FROM `". $syslogdb_default . "`.". $from_table ." + WHERE seq IN (" . $all_seq ."))"); + + $messages_moved = db_affected_rows($syslog_cnn); + + if ($messages_moved > 0) { + syslog_db_execute("DELETE FROM `". $syslogdb_default . "`.`" . $from_table ."` + WHERE seq IN (" . $all_seq .")" ); + } + + $xferred += $messages_moved; + $move_count = $messages_moved; + } + + $debugm = sprintf('Moved %5s - Message(s)', $move_count); + } - syslog_debug(sprintf('Removed %5s - Record(s) from incoming', $removed)); - syslog_debug(sprintf('Xferred %5s - Record(s) to the syslog table', $xferred)); + if ($sql_dlt != '') { + /* now delete the remainder that match */ + syslog_db_execute($sql_dlt); + $removed += db_affected_rows($syslog_cnn); + $debugm = sprintf('Deleted %5s Message(s)', $removed); + } + + syslog_debug($debugm); + } + } + } return array('removed' => $removed, 'xferred' => $xferred); } +/* get_hash_syslog - returns the current unique hash for an alert + @arg $id - (int) the ID of the syslog item to return a hash for + @returns - a 128-bit, hexadecimal hash */ +function get_hash_syslog($id, $table) { + $hash = syslog_db_fetch_cell_prepared('SELECT hash + FROM ' . $table . ' + WHERE id = ?', + array($id)); + + if (empty($hash)) { + return generate_hash(); + } elseif (preg_match('/[a-fA-F0-9]{32}/', $hash)) { + return $hash; + } else { + return generate_hash(); + } +} + +function syslog_ia2xml($array) { + $xml = ''; + + if (cacti_sizeof($array)) { + foreach ($array as $key=>$value) { + if (is_array($value)) { + $xml .= "\t<$key>" . syslog_ia2xml($value) . "\n"; + } else { + $xml .= "\t<$key>" . html_escape($value) . "\n"; + } + } + } + + return $xml; +} + +function syslog_array2xml($array, $tag = 'template') { + static $index = 1; + + $xml = "<$tag$index>\n" . syslog_ia2xml($array) . "\n"; + + $index++; + + return $xml; +} + +/** + * syslog_execute_ticket_command - run the configured ticketing command for an alert + * + * @param array $alert The alert row from syslog_alert table + * @param array $hostlist Hostnames matched by the alert + * @param string $error_message sprintf template used if exec() returns non-zero + * + * @return void + */ +function syslog_execute_ticket_command($alert, $hostlist, $error_message) { + $command = read_config_option('syslog_ticket_command'); + + if ($command != '') { + $command = trim($command); + } + + if ($alert['open_ticket'] == 'on' && $command != '') { + /* trim surrounding quotes so paths like "/usr/bin/cmd" resolve correctly */ + $cparts = preg_split('/\s+/', trim($command)); + $executable = trim($cparts[0], '"\''); + + if (cacti_sizeof($cparts) && is_executable($executable)) { + $command = $command . + ' --alert-name=' . cacti_escapeshellarg(clean_up_name($alert['name'])) . + ' --severity=' . cacti_escapeshellarg($alert['severity']) . + ' --hostlist=' . cacti_escapeshellarg(implode(',', $hostlist)) . + ' --message=' . cacti_escapeshellarg($alert['message']); + + $output = array(); + $return = 0; + + exec($command, $output, $return); + + if ($return !== 0) { + cacti_log(sprintf($error_message, $alert['name'], $return, implode(', ', $output)), false, 'SYSLOG'); + } + } else { + $reason = (strpos($executable, DIRECTORY_SEPARATOR) === false) + ? 'PATH-based lookups are not supported; use an absolute path' + : 'file not found or not marked executable'; + cacti_log("SYSLOG ERROR: Ticket command is not executable: '$command' -- $reason", false, 'SYSTEM'); + } + } +} + +/** + * syslog_execute_alert_command - run the per-alert shell command for a matched result + * + * @param array $alert The alert row from syslog_alert table + * @param array $results The matched syslog result row + * @param string $hostname Resolved hostname for the source device + * + * @return void + */ +function syslog_execute_alert_command($alert, $results, $hostname) { + /* alert_replace_variables() escapes each substituted token (, + * , , , , ) with + * cacti_escapeshellarg(). The command template itself comes from admin + * configuration ($alert['command']) and is trusted at that boundary. + * Do not introduce additional substitution paths that bypass this escaping. */ + $command = alert_replace_variables($alert, $results, $hostname); + + /* trim surrounding quotes so paths like "/usr/bin/cmd" resolve correctly */ + $cparts = preg_split('/\s+/', trim($command)); + $executable = trim($cparts[0], '"\''); + + $output = array(); + $return = 0; + + if (cacti_sizeof($cparts) && is_executable($executable)) { + exec($command, $output, $return); + + if ($return !== 0 && !empty($output)) { + cacti_log('SYSLOG NOTICE: Alert command output: ' . implode(', ', $output), true, 'SYSTEM'); + } + + if ($return !== 0) { + cacti_log(sprintf('ERROR: Alert command failed. Alert:%s, Exit:%s, Output:%s', $alert['name'], $return, implode(', ', $output)), false, 'SYSLOG'); + } + } else { + $reason = (strpos($executable, DIRECTORY_SEPARATOR) === false) + ? 'PATH-based lookups are not supported; use an absolute path' + : 'file not found or not marked executable'; + cacti_log("SYSLOG ERROR: Alert command is not executable: '$command' -- $reason", false, 'SYSTEM'); + } +} + +/** + * syslog_process_alerts - Process each of the Syslog Alerts + * + * Syslog Alerts come in essentially 4 types + * + * System Wide non-threshold alerts - These alerts are simply alerts that match the pattern defined by the alert + * System Wide threshold alerts - These alerts are syslog messages that both match the pattern and have more than the + * threshold amount that take place every collector cycle (30 seconds, 1 minutes, 5 minutes, etc) + * Host based non-threshold alerts - Alerts that happen on a per host basis, so you can alert for each host that the syslog message + * occurred to. + * Host based threshold alerts - Like the system level alert, it's an alert that happens more than x times per host. + * + * The advantage and reason for having host based alerts is that it allows you to target ticket generation for a specific host + * and more importantly, to be able to have a separate re-alert cycles for that very same message as there can be similar messages + * happening all the time at the system level, so it's hard to target a single host for re-alert rules. + * + * @param (int) The max_seq to process + * + * @return (array) An array of the number of alerts processed and the number of alerts generated + */ function syslog_process_alerts($max_seq) { global $syslogdb_default; @@ -475,16 +1221,27 @@ function syslog_process_alerts($max_seq) { $params = $sql_data['params']; if ($sql != '') { - $results = syslog_db_fetch_assoc_prepared($sql . $groupBy, $params); + if ($alert['level'] == '1') { + $th_sql = str_replace('*', 'host, COUNT(*) AS count', $sql); + $results = syslog_db_fetch_assoc_prepared($th_sql . $groupBy, $params); - if (cacti_sizeof($results)) { - foreach($results as $result) { - $syslog_alarms++; - syslog_debug(sprintf('Alert - Alert \'%s\' matched', $alert['name'])); + if (cacti_sizeof($results)) { + foreach($results as $result) { + $aparams = $params; + $aparams[] = $result['host']; - /* send the alert */ - syslog_send_alert($alert, $result); + $asql = $sql . ' AND host = ?'; + + $syslog_alarms += syslog_process_alert($alert, $asql, $aparams, $result['count'], $result['host']); + } } + } elseif ($alert['method'] == '1') { + $th_sql = str_replace('*', 'COUNT(*)', $sql); + $count = syslog_db_fetch_cell_prepared($th_sql . $groupBy, $params); + $syslog_alarms += syslog_process_alert($alert, $sql, $params, $count); + } else { + $count = 0; + $syslog_alarms += syslog_process_alert($alert, $sql, $params, $count); } } } @@ -493,6 +1250,353 @@ function syslog_process_alerts($max_seq) { return array('syslog_alerts' => $syslog_alerts, 'syslog_alarms' => $syslog_alarms); } +/** + * syslog_process_alert - Process the Alert and generate notifications, execute commands, etc. + * + * @param (array) The alert to process + * @param (string) The SQL to search for the Alert + * @param (array) The SQL parameters to be prepared into the SQL + * @param (int) In the case of a threshold alert, the number of occurrents + * of hosts with occurrences that were encountered through + * pre-processing the message + * @param (string) The hostname that this alert rule is for + * + * @return (int) '1' if the alert triggered, else '0' + */ +function syslog_process_alert($alert, $sql, $params, $count, $hostname = '') { + global $config, $severities, $syslog_levels; + + include_once($config['base_path'] . '/lib/reports.php'); + + $messese = ''; + $smsalert = ''; + + $alert_count = 0; + $syslog_alarms = 0; + $hostlist = array(); + $max_alerts = read_config_option('syslog_maxrecords'); + $report_tag = false; + $theme = false; + $format_ok = false; + + syslog_debug('-------------------------------------------------------------------------------------'); + syslog_debug(sprintf('Processing - %s', $alert['name'])); + + if (read_config_option('syslog_html') == 'on') { + $html = true; + $format_ok = reports_load_format_file(read_config_option('syslog_format_file'), $output, $report_tag, $theme); + + syslog_debug('Format/CSS ' . ($format_ok ? 'Ok':'Not Ok') . ' - Report Tag ' . ($report_tag ? 'included':'missing')); + } else { + $html = false; + } + + /** + * format the from Email address + */ + $from_email = read_config_option('settings_from_email'); + if ($from_email == '') { + $from_email = 'Cacti@cacti.net'; + } + + $from_name = read_config_option('settings_from_name'); + if ($from_name == '') { + $from_name = 'Cacti Reporting'; + } + + $from = array($from_email, $from_name); + + /** + * format the destination Email addresses + */ + $alert['email'] = trim($alert['email'], ', '); + if ($alert['notify'] > 0) { + $additional = db_fetch_cell_prepared('SELECT emails + FROM plugin_notification_lists + WHERE id = ?', + array($alert['notify'])); + + if ($additional != '') { + $alert['email'] .= ', ' . trim($additional, ' ,'); + } + } + + /** + * process the alert now. + */ + if (($alert['method'] == '1' && $count >= $alert['num']) || $alert['method'] == '0') { + $at = syslog_db_fetch_assoc_prepared($sql, $params); + + /** + * get a date for the repeat alert + */ + if ($alert['repeat_alert']) { + $date = date('Y-m-d H:i:s', time() - ($alert['repeat_alert'] * read_config_option('poller_interval'))); + } else { + $date = ''; + } + + /** + * The finalized email or test message. + */ + $message = ''; + + /** + * A list of all messages from the alert + */ + $results = array(); + + syslog_debug(sprintf('Found %5s - Matching Records.', cacti_sizeof($at))); + + if (cacti_sizeof($at)) { + if ($html) { + if (!$format_ok) { + $message .= "'; + } + + if ($alert['method'] == '1') { + if ($alert['body'] == '') { + if ($hostname != '') { + $message .= '

' . __esc('Cacti Syslog Threshold Alert \'%s\' for Host \'%s\'', $alert['name'], $hostname, 'syslog') . '

'; + } else { + $message .= '

' . __esc('Cacti Syslog Threshold Alert \'%s\'', $alert['name'], 'syslog') . '

'; + } + } else { + $message .= '
' . $alert['body'] . '
'; + } + + $message .= ''; + $message .= ' + + + + + + '; + + $message .= ''; + $message .= ''; + $message .= ''; + $message .= ''; + $message .= '
' . __('Alert Name', 'syslog') . '' . __('Severity', 'syslog') . '' . __('Threshold', 'syslog') . '' . __('Count', 'syslog') . '' . __('Match String', 'syslog') . '
' . html_escape($alert['name']) . '' . $severities[$alert['severity']] . '' . $alert['num'] . '' . sizeof($at) . '' . html_escape($alert['message']) . '

'; + } else { + if ($alert['body'] == '') { + if ($hostname != '') { + $message .= '

' . __esc('Cacti Syslog Alert \'%s\' for Host \'%s\'', $alert['name'], $hostname, 'syslog') . '

'; + } else { + $message .= '

' . __esc('Cacti Syslog Alert \'%s\'', $alert['name'], 'syslog') . '

'; + } + } else { + $message .= '
' . $alert['body'] . '
'; + } + } + + $message .= ''; + $message .= ' + + + + + + '; + } else { + if ($alert['method'] == '1') { + if ($alert['body'] == '') { + $message .= '---------------------------------------------------------------------' . PHP_EOL . PHP_EOL; + if ($hostname != '') { + $message .= __('WARNING: A Syslog Threshold Alert has Been Triggered for Host \'%s\'', $hostname, 'syslog') . PHP_EOL . PHP_EOL; + } else { + $message .= __('WARNING: A Syslog Threshold Alert has Been Triggered', 'syslog') . PHP_EOL . PHP_EOL; + } + } else { + $message .= '---------------------------------------------------------------------' . PHP_EOL . PHP_EOL; + $message .= $alert['body'] . PHP_EOL; + } + + $message .= __('Name:', 'syslog') . ' ' . html_escape($alert['name']) . PHP_EOL; + $message .= __('Severity:', 'syslog') . ' ' . $severities[$alert['severity']] . PHP_EOL; + $message .= __('Threshold:', 'syslog') . ' ' . $alert['num'] . PHP_EOL; + $message .= __('Count:', 'syslog') . ' ' . sizeof($at) . PHP_EOL; + $message .= __('Message String:', 'syslog') . ' ' . html_escape($alert['message']) . PHP_EOL; + } else { + if ($alert['body'] == '') { + if ($hostname != '') { + $message .= __esc('Cacti Syslog Alert \'%s\' for Host \'%s\'', $alert['name'], $hostname, 'syslog'); + } else { + $message .= __esc('Cacti Syslog Alert \'%s\'', $alert['name'], 'syslog'); + } + } else { + $message .= '---------------------------------------------------------------------' . PHP_EOL . PHP_EOL; + $message .= $alert['body']; + } + } + } + + $hmessage = $message; + $plogged = false; + $flogged = false; + + foreach($at as $a) { + $hostlist[] = $a['host']; + $results['message'] = (isset($results['message']) ? $results['message'] . ', ':'') . $a['message']; + + if (isset($results['priority_id']) && $results['priority_id'] != $a['priority_id'] && !$plogged) { + cacti_log(sprintf('Alert \'%s\' has more than one priority id, last one experienced will be leveraged', $alert['name']), false, 'SYSLOG'); + $plogged = true; + } + + if (isset($results['facility_id']) && $results['facility_id'] != $a['facility_id'] && !$flogged) { + cacti_log(sprintf('Alert \'%s\' has more than one facility id, last one experienced will be leveraged', $alert['name']), false, 'SYSLOG'); + $flogged = true; + } + + $results['priority_id'] = $a['priority_id']; + $results['facility_id'] = $a['facility_id']; + + if (($alert['method'] == 1 && $alert_count < $max_alerts) || $alert['method'] == 0) { + if ($alert['method'] == 0) { + $message = $hmessage; + } + + if ($html) { + $message .= ' + + + + + + '; + } else { + $message .= '---------------------------------------------------------------------' . PHP_EOL . PHP_EOL; + $message .= __('Hostname:', 'syslog') . ' ' . html_escape($a['host']) . PHP_EOL; + $message .= __('Date:', 'syslog') . ' ' . $a['logtime'] . PHP_EOL; + $message .= __('Severity:', 'syslog') . ' ' . $severities[$alert['severity']] . PHP_EOL . PHP_EOL; + $message .= __('Level:', 'syslog') . ' ' . $syslog_levels[$a['priority_id']] . PHP_EOL . PHP_EOL; + $message .= __('Message:', 'syslog') . ' ' . PHP_EOL . $a['message'] . PHP_EOL; + } + } + } + + $hostlist = array_unique($hostlist); + + $syslog_alarms++; + $alert_count++; + + $send = true; + $found = false; + + /** + * If this is a repeat alert type threshold, then check to + * see if it's time to re-alert. + */ + if ($alert['repeat_alert'] > 0) { + if ($hostname != '') { + $found = syslog_db_fetch_cell_prepared('SELECT COUNT(*) + FROM syslog_logs + WHERE alert_id = ? + AND logtime > ? + AND host = ?', + array($alert['id'], $date, $hostname)); + } else { + $found = syslog_db_fetch_cell_prepared('SELECT COUNT(*) + FROM syslog_logs + WHERE alert_id = ? + AND logtime > ? + AND host = "system"', + array($alert['id'], $date)); + } + } + + if ($found) { + $send = false; + } + + if ($html) { + $message .= '
' . __('Hostname', 'syslog') . '' . __('Date', 'syslog') . '' . __('Severity', 'syslog') . '' . __('Level', 'syslog') . '' . __('Message', 'syslog') . '
' . html_escape($a['host']) . '' . $a['logtime'] . '' . $severities[$alert['severity']] . '' . $syslog_levels[$a['priority_id']] . '' . html_escape($a['message']) . '
'; + } else { + $message .= '---------------------------------------------------------------------' . PHP_EOL . PHP_EOL; + } + + if ($html) { + if ($format_ok) { + if ($report_tag) { + $message = str_replace('', $message, $output); + } else { + $message = $output . $message . ''; + } + } else { + $message = '' . $message . ''; + } + } + + /** + * This is a Traditional syslog alert where all matching messages + * will be reported in the notification. + */ + if ($alert['method'] == '0') { + if ($send) { + $sequence = syslog_log_alert($alert['id'], $alert['name'], $alert['severity'], $a, 1, $message); + + $smsalert = __('Sev:', 'syslog') . $severities[$alert['severity']] . __(', Host:', 'syslog') . $a['host'] . __(', URL:', 'syslog') . read_config_option('base_url', true) . '/plugins/syslog/syslog.php?tab=current&id=' . $sequence; + + /** + * Send the Email notification + */ + if ($alert['email'] != '' || $smsalert != '') { + syslog_sendemail(trim($alert['email']), $from, __esc('Event Alert - %s', $alert['name'], 'syslog'), $message, $smsalert); + } + + alert_setup_environment($alert, $results, $hostlist, $hostname); + + /** + * Open a ticket if this options have been selected. + */ + syslog_execute_ticket_command($alert, $hostlist, 'ERROR: Ticket Command Failed. Alert:%s, Exit:%s, Output:%s'); + + if (trim($alert['command']) != '' && !$found) { + syslog_execute_alert_command($alert, $results, $hostname); + } + + } + } elseif ($alert['method'] == 1) { + if ($send) { + /** + * Send the Email notification + */ + if ($alert['email'] != '' || $smsalert != '') { + syslog_sendemail(trim($alert['email']), $from, __esc('Event Alert - %s', $alert['name'], 'syslog'), $message, $smsalert); + } + + $sequence = syslog_log_alert($alert['id'], $alert['name'], $alert['severity'], $at[0], sizeof($at), $message, $hostlist); + $smsalert = __('Sev:', 'syslog') . $severities[$alert['severity']] . __(', Count:', 'syslog') . sizeof($at) . __(', URL:', 'syslog') . read_config_option('base_url', true) . '/plugins/syslog/syslog.php?tab=current&id=' . $sequence; + + alert_setup_environment($alert, $results, $hostlist, $hostname); + + syslog_execute_ticket_command($alert, $hostlist, 'ERROR: Command Failed. Alert:%s, Exit:%s, Output:%s'); + + if (trim($alert['command']) != '' && !$found) { + syslog_execute_alert_command($alert, $results, $hostname); + } + } + } + + syslog_debug("Alert Rule '" . $alert['name'] . "' has been triggered"); + } + } + + return $alert_count; +} + +/** + * syslog_get_alert_sql - Get the SQL and params for the alert to + * checi. + * + * @param (array) The alert attributes to process + * + * @return (array) The SQL and the prepared array for the SQL + */ function syslog_get_alert_sql(&$alert, $max_seq) { global $syslogdb_default, $syslog_incoming_config; @@ -574,6 +1678,15 @@ function syslog_get_alert_sql(&$alert, $max_seq) { return array('sql' => $sql, 'params' => $params); } +/** + * syslog_preprocess_incoming_records - Generate a max_seq to allow moving of + * records to done table and mark incoming records with the max_seq and + * then if syslog is configured to strip domains, perform that first. + * + * @return (int) Unique id to allow syslog messages that come in randomly to + * be differentiate between messages to process and messages + * to be left till then ext polling cycle. + */ function syslog_preprocess_incoming_records() { global $syslogdb_default; @@ -609,6 +1722,14 @@ function syslog_preprocess_incoming_records() { return array('max_seq' => 0, 'incoming' => 0); } +/** + * syslog_strip_incoming_domains - If syslog is setup to strip DNS domain name suffixes do that + * prior to processing the records. + * + * @param (string) The max_seq records to process + * + * @return (void) + */ function syslog_strip_incoming_domains($max_seq) { global $syslogdb_default; @@ -623,11 +1744,22 @@ function syslog_strip_incoming_domains($max_seq) { WHERE host LIKE ? AND `status` = 1 AND `seq` <= ?', - array(\'%\' . $domain, $max_seq)); + array('%' . $domain, $max_seq)); } } } +/** + * Check if the hostname is in the cacti hosts table + * Some devices only send IP addresses in syslog messages, and may not be in the DNS + * however they may be in the cacti hosts table as monitored devices. + * + * @param (string) The hostname to check + * @param (int) The max_seq for syslog_incoming messages to process + * + * @return (bool) True if the host exists in the Cacti database, false otherwise + */ + function syslog_check_cacti_hosts($host, $max_seq) { global $syslogdb_default; @@ -649,13 +1781,25 @@ function syslog_check_cacti_hosts($host, $max_seq) { AND `status` = 1 AND `seq` <= ?', array($cacti_host['description'], $host, $max_seq)); - + return true; } - + return false; } - +/** + * syslog_update_reference_tables - There are many values in the syslog plugin + * that for the purposes of reducing the size of the syslog table are normalized + * the columns includes the facility, the priority, and the hostname. + * + * This function will add those new hostnames to the various reference tables + * and assign an id to each of them. This way the syslog table can be optimized + * for size as much as possible. + * + * @param (int) The max_seq for syslog_incoming messages to process + * + * @return (void) + */ function syslog_update_reference_tables($max_seq) { global $syslogdb_default; @@ -663,46 +1807,46 @@ function syslog_update_reference_tables($max_seq) { syslog_debug('Updating Reference Tables from New Syslog Records'); /* Validate and resolve hostnames - check DNS first, then Cacti, then mark invalid */ - if (read_config_option('syslog_resolve_hostname') == 'on') { - $hosts = syslog_db_fetch_assoc_prepared('SELECT DISTINCT host - FROM `' . $syslogdb_default . '`.`syslog_incoming` - WHERE `status` = 1 + if (read_config_option('syslog_resolve_hostname') == 'on') { + $hosts = syslog_db_fetch_assoc_prepared('SELECT DISTINCT host + FROM `' . $syslogdb_default . '`.`syslog_incoming` + WHERE `status` = 1 AND `seq` <= ?', - array($max_seq)); - - foreach($hosts as $host) { - if (!isset($host['host']) || empty($host['host'])) { - continue; - } - - $resolved = false; - - // Check if hostname resolves via DNS (only if DNS is enabled) - if (read_config_option('syslog_no_dns') != 'on') { - if ($host['host'] != gethostbyname($host['host'])) { - // DNS resolved successfully - $resolved = true; - } - } - - // Check if hostname exists in Cacti hosts table (only if not already resolved via DNS) - if (!$resolved) { - $resolved = syslog_check_cacti_hosts($host['host'], $max_seq); - } - - // If not resolved via DNS or found in Cacti, prefix the hostname - if (!$resolved) { - $unresolved_host = 'unresolved-' . $host['host']; - cacti_log("SYSLOG WARNING: Hostname '" . $host['host'] . "' could not be resolved via DNS or found in Cacti hosts table, marking as '" . $unresolved_host . "'", false, 'SYSLOG'); - syslog_db_execute_prepared('UPDATE `' . $syslogdb_default . "`.`syslog_incoming` - SET host = ? - WHERE host = ? - AND `status` = 1 + array($max_seq)); + + foreach($hosts as $host) { + if (!isset($host['host']) || empty($host['host'])) { + continue; + } + + $resolved = false; + + // Check if hostname resolves via DNS (only if DNS is enabled) + if (read_config_option('syslog_no_dns') != 'on') { + if ($host['host'] != gethostbyname($host['host'])) { + // DNS resolved successfully + $resolved = true; + } + } + + // Check if hostname exists in Cacti hosts table (only if not already resolved via DNS) + if (!$resolved) { + $resolved = syslog_check_cacti_hosts($host['host'], $max_seq); + } + + // If not resolved via DNS or found in Cacti, prefix the hostname + if (!$resolved) { + $unresolved_host = 'unresolved-' . $host['host']; + cacti_log("SYSLOG WARNING: Hostname '" . $host['host'] . "' could not be resolved via DNS or found in Cacti hosts table, marking as '" . $unresolved_host . "'", false, 'SYSLOG'); + syslog_db_execute_prepared('UPDATE `' . $syslogdb_default . "`.`syslog_incoming` + SET host = ? + WHERE host = ? + AND `status` = 1 AND `seq` <= ?", - array($unresolved_host, $host['host'], $max_seq)); - } - } - } + array($unresolved_host, $host['host'], $max_seq)); + } + } + } syslog_db_execute_prepared('INSERT INTO `' . $syslogdb_default . '`.`syslog_programs` (program, last_updated) @@ -738,13 +1882,21 @@ function syslog_update_reference_tables($max_seq) { ) AS s INNER JOIN `" . $syslogdb_default . '`.`syslog_hosts` AS sh ON s.host = sh.host - ) AS merge + ) ON DUPLICATE KEY UPDATE host_id=VALUES(host_id), last_updated=NOW()', array($max_seq)); } +/** + * syslog_update_statistics - Insert new statistics rows into the syslog statistics + * table for post review + * + * @param (int) The max_seq for all syslog incoming records to be processed + * + * @return (void) + */ function syslog_update_statistics($max_seq) { global $syslogdb_default, $syslog_cnn; @@ -758,8 +1910,8 @@ function syslog_update_statistics($max_seq) { ON sh.host=si.host INNER JOIN syslog_programs AS sp ON sp.program=si.program - WHERE `status` = 1 - AND `seq` <= ? + WHERE si.`status` = 1 + AND si.`seq` <= ? GROUP BY host_id, priority_id, facility_id, program_id) AS merge GROUP BY host_id, priority_id, facility_id, program_id', array($max_seq)); @@ -770,12 +1922,23 @@ function syslog_update_statistics($max_seq) { } } +/** + * syslog_incoming_to_syslog - Move incoming syslog records to the syslog table + * + * Once all Alerts have been processed, we need to move entries first to + * the syslog table, and then after which we can perform various + * removal rules against them. + * + * @param (int) The max_seq for rows in the syslog table + * + * @return (int) The number of rows moved to the syslog table + */ function syslog_incoming_to_syslog($max_seq) { global $syslogdb_default, $syslog_cnn; syslog_db_execute_prepared('INSERT INTO `' . $syslogdb_default . '`.`syslog` (logtime, priority_id, facility_id, program_id, host_id, message) - SELECT logtime, priority_id, facility_id, sp.program_id, sh.host_id, message + SELECT logtime, priority_id, facility_id, program_id, host_id, message FROM ( SELECT logtime, priority_id, facility_id, sp.program_id, sh.host_id, message FROM syslog_incoming AS si @@ -783,8 +1946,8 @@ function syslog_incoming_to_syslog($max_seq) { ON sh.host = si.host INNER JOIN syslog_programs AS sp ON sp.program = si.program - WHERE `status` = 1 - AND `seq` <= ? + WHERE si.`status` = 1 + AND si.`seq` <= ? ) AS merge', array($max_seq)); @@ -810,10 +1973,6 @@ function syslog_incoming_to_syslog($max_seq) { return array('moved' => $moved, 'stale' => $stale); } - syslog_debug(sprintf('Deleted %5s - Stale Message(s) from incoming', $stale)); - - return array('moved' => $moved, 'stale' => $stale); -} /** * syslog_postprocess_tables - Remove stale records and optimize tables after