#!/usr/bin/env php $e->getMessage(), 'file' => $e->getFile() . ':' . $e->getLine(), ]); fwrite(STDERR, sprintf( "[FEHLER] %s: %s\n %s:%d\n", get_class($e), $e->getMessage(), $e->getFile(), $e->getLine() )); exit(1); }); $options = parseArguments($argv); if ($options['help']) { printUsage(); exit(0); } // --------------------------------------------------------------------------- // Ueberlappende Laeufe verhindern. // // Bei minuetlichem Cron und einem Lauf, der laenger als eine Minute braucht - // etwa weil die Datenbank klemmt oder viele Webhooks zugestellt werden - // starten sonst mehrere Evaluatoren gleichzeitig. Die wuerden dieselben // Zustandswechsel doppelt protokollieren und doppelt alarmieren. // --------------------------------------------------------------------------- $lockHandle = null; if (!$options['no-lock']) { $lockFile = DC_VAR . '/watchdog-tick.lock'; if (!is_dir(DC_VAR)) { @mkdir(DC_VAR, 0775, true); } $lockHandle = @fopen($lockFile, 'c'); if ($lockHandle === false) { fwrite(STDERR, "[FEHLER] Sperrdatei nicht beschreibbar: {$lockFile}\n"); exit(1); } if (!flock($lockHandle, LOCK_EX | LOCK_NB)) { // Kein Fehler, sondern der Normalfall bei einem langsamen Vorlauf. if (!$options['quiet']) { fwrite(STDERR, "[UEBERSPRUNGEN] Es laeuft bereits ein Tick.\n"); } exit(2); } // Der Rueckgabewert von ftruncate ist hier ohne Belang: die Datei traegt // nur die PID zur Fehlersuche, ihr Inhalt steuert nichts. ftruncate($lockHandle, 0); fwrite($lockHandle, (string)getmypid()); fflush($lockHandle); } // Kein try/finally um den Lauf: exit() fuehrt in PHP keine finally-Bloecke aus, // die Freigabe waere dort also nur zum Schein notiert. Der Handler oben beendet // den Prozess bei einer Exception - dabei gibt das Betriebssystem die Sperre // ohnehin frei. Fuer den regulaeren Weg steht die Freigabe am Ende. $startedAt = microtime(true); $db = Db::init(); $result = Evaluator::run($db); $wallMs = (int)round((microtime(true) - $startedAt) * 1000); if ($options['json']) { echo json_encode( $result + ['wall_ms' => $wallMs], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE ), PHP_EOL; } elseif (!($options['quiet'] && $result['changed'] === 0)) { // Im Cron soll nur etwas ankommen, wenn es etwas zu berichten gibt - // sonst erzeugt ein minuetlicher Job 1440 Mails am Tag. printSummary($result, $wallMs); } releaseLock($lockHandle); exit(0); // =========================================================================== /** * @param list $argv * @return array{help:bool,json:bool,quiet:bool,no-lock:bool} */ function parseArguments(array $argv): array { $options = ['help' => false, 'json' => false, 'quiet' => false, 'no-lock' => false]; foreach (array_slice($argv, 1) as $arg) { switch ($arg) { case '-h': case '--help': $options['help'] = true; break; case '-j': case '--json': $options['json'] = true; break; case '-q': case '--quiet': $options['quiet'] = true; break; case '--no-lock': $options['no-lock'] = true; break; default: fwrite(STDERR, "Unbekannte Option: {$arg}\n\n"); printUsage(); exit(1); } } return $options; } /** * Gibt die Sperre frei. Ohne aktive Sperre ein No-Op. * * @param resource|null $handle */ function releaseLock($handle): void { if ($handle === null) { return; } flock($handle, LOCK_UN); fclose($handle); } /** @param array $result */ function printSummary(array $result, int $wallMs): void { printf( "Watchdog-Tick: %d Monitor(e) geprueft, %d Zustandswechsel in %d ms (gesamt %d ms).\n", $result['checked'], $result['changed'], $result['duration_ms'], $wallMs ); foreach ($result['changes'] as $change) { printf( " %-28s %s -> %-8s %s%s\n", $change['source'], $change['from'], $change['to'], $change['reason'], $change['suppressed'] !== null ? sprintf(' [Alarm unterdrueckt, %s ist selbst unten]', $change['suppressed']) : '' ); } if (($result['released_leases'] ?? 0) > 0) { printf(" %d abgelaufene Bugtracker-Lease(s) freigegeben.\n", $result['released_leases']); } if (($result['purged_metrics'] ?? 0) > 0) { printf(" %d alte Metrikwerte abgeraeumt.\n", $result['purged_metrics']); } } function printUsage(): void { echo <<