Skip to content

bug: Restore Cacti 1.2.23 compatibility for the 4.11 upgrade (array primary keys and process helpers) - #439

Merged
TheWitness merged 7 commits into
developfrom
fix/process-helper-compat
Nov 11, 2026
Merged

TheWitness merged 7 commits into
developfrom
fix/process-helper-compat

Conversation

@TheWitness

@TheWitness TheWitness commented Nov 11, 2026 •

Copy link
Copy Markdown
Member

Description

Upgrading Syslog from 4.2 to 4.11 on a current Cacti release (reported on 1.2.30 / PHP 8.3) crashes partway through and then disables the plugin. Two independent spots in 4.11 assume a Cacti core that is newer than the plugin's declared compat = 1.2.23. This PR restores the 1.2.23 floor.

1. Table creation fataled with implode(): Argument #1 must be array, string given

Cacti core's db_table_create() mishandled an array primary key before 1.2.32: a typo passed implode() the glue string concatenated with the array (implode(' + "," + ' . $data['primary'])) instead of the column list. Creating a table with an array primary therefore fataled. The plugin passes array primaries to db_update_table() when creating its dashboard, status, saved-search and share tables during the upgrade, so the upgrade crashed on every current release (the bug is present in 1.2.30 and 1.2.31; Cacti fixed it for 1.2.32).

Fix: route those calls through a new syslog_db_update_table() wrapper. When a table does not yet exist on a Cacti older than 1.2.32, it rewrites the array primary to the back-tick-joined string form the create path accepts, dodging the typo. The array form is preserved when the table already exists (db_update_table() reconciles the primary with array_diff(), which requires an array) and on Cacti 1.2.32+ where the create path is fixed. A version/existence latch, not a blanket string conversion, is required because the existing-table path would otherwise array_diff() a string and fatal.

2. Poller fataled with Call to undefined function cacti_process_still_running()

cacti_process_still_running() and cacti_process_kill() live in Cacti core lib/poller.php, but were added after the 1.2.31 release (they ship in 1.2.32+). The plugin called both unguarded in includes/functions.php (syslog_workers_running) and syslog_process.php (syslog_kill_workers), so the poller fataled and Cacti disabled the syslog plugin.

Fix: add function_exists()-guarded, posix_kill()-based fallbacks used only when core does not provide the helpers; on 1.2.32+ the native implementations are used. posix_kill() is always available because Cacti core defines a win32 shim for it.

Related Issue

Reported on the Cacti forums: https://forums.cacti.net/viewtopic.php?t=64912 (upgrade Syslog 4.2 → 4.11 on Cacti 1.2.30 / PHP 8.3). Both fatals in that thread — the implode() table-creation crash and the cacti_process_still_running() crash — are addressed here.

Motivation and Context

Without this change, Syslog 4.11 cannot be upgraded onto or run its poller on any current Cacti release (1.2.23 – 1.2.31), even though the plugin advertises compatibility back to 1.2.23.

How Has This Been Tested?

  • php -l clean on includes/functions.php, includes/schema.php, and the new test (PHP 8.3.36).
  • Full plugin suite via Cacti's own Composer vendor tree, plugin staged as a real dir at plugins/syslog:
    php include/vendor/bin/pest --configuration plugins/syslog/phpunit.xml
    → 635 passed (2658 assertions).
  • New tests/Unit/SchemaPrimaryKeyCompatTest.php covers the version/existence latch: string primary when creating on < 1.2.32, array primary when the table exists, array primary on 1.2.32+, and back-tick-joined composite keys on older Cacti.
  • Existing Unit/SyslogWorkerWaitTest exercises the cacti_process_still_running() path.

Testing environment: AlmaLinux 9, PHP 8.3.36, Cacti 1.2.x vendor tree.

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)

Checklist:

  • My code follows the code style of this project.
  • My change requires a change to the documentation.
  • I have updated the documentation repository accordingly.

Fixes #440
Fixes #441

cacti_process_still_running() and cacti_process_kill() were added to Cacti
core (lib/poller.php) after the 1.2.31 release and only ship in 1.2.32+.
The syslog plugin declares compat 1.2.23 but called both helpers unguarded
in includes/functions.php (syslog_workers_running) and syslog_process.php
(syslog_kill_workers). On every current Cacti release this produced a fatal
'Call to undefined function cacti_process_still_running()' from the poller,
which disabled the plugin.

Add function_exists()-guarded, posix_kill()-based fallbacks that are used
only when core does not provide the helpers; on 1.2.32+ the native core
implementations continue to be used. posix_kill() is always available
because Cacti core defines a win32 shim for it.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI balanced review requested due to automatic review settings November 11, 2026 15:04

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Critical PID validation gaps can misidentify unrelated processes as workers and allow oversized PIDs to trigger SIGTERM against every process the poller user can signal.

2 open issues
What changed in this PR

This PR adds compatibility fallbacks for Cacti process helpers to prevent Syslog poller failures on older supported Cacti releases.

Changes:

  • Adds guarded, posix_kill()-based fallbacks for process liveness checks and signaling.
  • Preserves existing core helpers when already defined.
  • Records the compatibility fix in the changelog.
File Description
includes/​functions.php Adds fallback implementations for both process helpers.
CHANGELOG.md Documents the older-release compatibility fix.

Review effort: Balanced

Comment thread includes/functions.php Resolved
Comment thread includes/functions.php Resolved
Cacti core's db_table_create() mishandled an array primary key before the
1.2.32 release: a typo passed implode() the glue string concatenated with
the array instead of the column list, so creating a table with an array
primary fataled with "implode(): Argument #1 must be of type array, string
given". The plugin declares compat 1.2.23 and passes array primaries to
db_update_table() when creating its dashboard, status, saved-search and
share tables during the 4.2 -> 4.11 upgrade, so the upgrade crashed on every
current Cacti release.

Route those calls through a new syslog_db_update_table() wrapper. When a
table does not yet exist on a Cacti older than 1.2.32 it rewrites the array
primary to the back-tick joined string form the create path accepts, dodging
the core typo. The array form is preserved when the table already exists
(db_update_table() reconciles the primary with array_diff(), which requires
an array) and on Cacti 1.2.32+ where the create path is fixed.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@TheWitness
TheWitness force-pushed the fix/process-helper-compat branch from e13b65a to e38a0bc Compare November 11, 2026 15:17
@TheWitness TheWitness changed the title bug: Polyfill Cacti process helpers for releases older than 1.2.32 bug: Restore Cacti 1.2.23 compatibility for the 4.11 upgrade (array primary keys and process helpers) Nov 11, 2026
TheWitness and others added 3 commits November 11, 2026 12:08
…rsized PIDs

Address review feedback on the compatibility shims for Cacti releases older
than 1.2.32:

- cacti_process_still_running() now confirms command identity through procfs
  (cmdline) and only falls back to a bare signal-0 liveness probe when that
  identity cannot be determined, so a dead worker's PID that has been reused
  by an unrelated process is no longer reported as a live worker.
- Both fallbacks validate the PID against the platform pid_t range before
  calling posix_kill(). An oversized processes.pid value (e.g. 4294967295 on a
  64-bit column) would otherwise narrow to pid_t -1 and make SIGTERM target
  every process the poller user can signal.

The validation and identity logic mirror Cacti core's cacti_process_pid_is_valid()
and cacti_process_identity_matches(), reproduced here because those helpers are
also absent before 1.2.32.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
TheWitness and others added 2 commits November 11, 2026 13:39
The patch-coverage gate requires 100 percent coverage of changed lines in
includes/functions.php, but the process-helper polyfills had uncovered
branches: the non-scalar PID rejection, the procfs command-identity match,
and cacti_process_kill(). The identity check read /proc directly and compared
against the test runner cmdline, so its hash_equals() and parser branches
could never be exercised deterministically in the isolated Pest process.

Extract the cmdline parser into syslog_compat_script_from_cmdline() and give
syslog_compat_pid_identity_matches() an optional injectable reader (defaulting
to the real /proc read) so tests drive every branch with synthetic cmdlines.
Expand ProcessHelperCompatTest to cover the non-scalar PID path, the parser,
the identity match/mismatch/unresolvable cases, and cacti_process_kill().
Production behavior is unchanged.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The earlier fix for the undefined syslog_db_update_table() in the CLI schema
regression test pulled the real wrapper in with a third eval(). SonarCloud
rule S1523 flags dynamic code execution, so the new eval() raised a high
severity security alert on the PR and failed the Quality Gate.

includes/schema.php is allowlisted from the patch-coverage gate and the
array-primary wrapper has dedicated coverage in SchemaPrimaryKeyCompatTest,
so the regression test does not need to execute the real wrapper. Define a
forwarding stub that delegates to the already-stubbed db_update_table(),
matching the other stubs in this file, which keeps syslog_setup_table_new()
runnable without adding another eval().

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@sonarqubecloud

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Syslog 4.11 disables on Cacti 1.2.30 Upgrade from 4.2 to 4.11 fails due to non-existent tables

4 participants