Files
2026-10-25 17:46:58 +00:00

151 lines
6.7 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
/**
* includes/crontab_installer.php
*
* Installiert/entfernt die 6 DeskHub-Cronjobs direkt in der System-Crontab des
* aktuellen Benutzers (crontab -l / crontab -). Nur sinnvoll auf einem eigenen
* (v)Server mit Shell-Zugriff für den Web-User – auf klassischem Shared-Hosting
* ist exec()/proc_open() fast immer gesperrt, siehe crontab_capability_check().
*
* Sicherheitsprinzip: Es wird NIE ein von außen beeinflussbarer String als
* Shell-Befehl ausgeführt. Die Crontab-Zeilen sind fest im Code hinterlegt
* (nur der erkannte PHP-Pfad und der DeskHub-Installationspfad sind variabel,
* beide serverseitig ermittelt, nicht aus einer Nutzereingabe). Nur der klar
* markierte "# DESKHUB-CRON-…"-Block wird verwaltet, alle anderen vorhandenen
* Crontab-Einträge des Nutzers bleiben unangetastet.
*/
const DESKHUB_CRON_MARKER_START = '# DESKHUB-CRON-START (automatisch verwaltet von DeskHub – Einstellungen → Cron-Jobs. Manuelle Änderungen hier werden beim erneuten Installieren überschrieben.)';
const DESKHUB_CRON_MARKER_END = '# DESKHUB-CRON-END';
/** Prüft, ob dieser Server eine automatische Installation überhaupt technisch zulässt. */
function crontab_capability_check(): array
{
// Crontab ist ein Unix-Mechanismus; auf Windows (z.B. lokale Entwicklung) von vornherein ausschließen,
// statt uns auf lokalisierte/unvorhersehbare Fehlertexte von cmd.exe verlassen zu müssen.
if (PHP_OS_FAMILY === 'Windows') {
return ['available' => false, 'reason' => 'Windows-Server werden nicht unterstützt (Crontab ist ein Unix/Linux-Mechanismus).'];
}
$disabled = array_map('trim', explode(',', (string) ini_get('disable_functions')));
foreach (['proc_open', 'shell_exec'] as $fn) {
if (!function_exists($fn) || in_array($fn, $disabled, true)) {
return ['available' => false, 'reason' => "PHP-Funktion \"$fn\" ist auf diesem Server gesperrt (typisch für Shared-Hosting)."];
}
}
$out = @shell_exec('crontab -l 2>&1');
if ($out === null) {
return ['available' => false, 'reason' => 'Der Befehl "crontab" konnte nicht ausgeführt werden.'];
}
// "no crontab for <user>" ist der normale Zustand, wenn noch keine Crontab existiert – das ist OK.
if (stripos($out, 'not found') !== false || stripos($out, 'command not found') !== false) {
return ['available' => false, 'reason' => 'Das Programm "crontab" ist auf diesem Server nicht installiert.'];
}
if (stripos($out, 'permission denied') !== false) {
return ['available' => false, 'reason' => 'Der Web-Server-Benutzer hat keine Berechtigung für crontab.'];
}
return ['available' => true, 'reason' => ''];
}
/** Versucht, den echten CLI-PHP-Pfad zu ermitteln (PHP_BINARY zeigt unter Apache/FPM auf den falschen Binary). */
function detect_php_cli_binary(): string
{
$which = trim((string) @shell_exec('command -v php 2>/dev/null'));
if ($which !== '' && is_executable($which)) {
return $which;
}
foreach (['/usr/bin/php', '/usr/local/bin/php', '/opt/php/bin/php'] as $candidate) {
if (is_executable($candidate)) {
return $candidate;
}
}
return 'php'; // Fallback: hoffen, dass PATH im Cron-Kontext passt
}
/** Baut den verwalteten DeskHub-Block mit den Job-Zeilen (fest hinterlegte Kommandos, keine Nutzereingabe). */
function crontab_marker_block(): string
{
$php = detect_php_cli_binary();
$root = realpath(__DIR__ . '/..');
$lines = [
DESKHUB_CRON_MARKER_START,
"*/5 * * * * $php $root/cron/fetch_emails.php >> $root/cron/fetch_emails.log 2>&1",
"* * * * * $php $root/cron/status_checks.php >> $root/cron/status_checks.log 2>&1",
"* * * * * $php $root/cron/run_plugins.php >> $root/cron/run_plugins.log 2>&1",
"*/15 * * * * $php $root/cron/check_sla.php >> $root/cron/check_sla.log 2>&1",
"0 * * * * $php $root/cron/check_escalations.php >> $root/cron/check_escalations.log 2>&1",
"0 6 * * * $php $root/cron/process_recurring.php >> $root/cron/process_recurring.log 2>&1",
"0 2 * * * $php $root/cron/process_backups.php >> $root/cron/process_backups.log 2>&1",
"0 3 * * * $php $root/cron/anonymize_data.php >> $root/cron/anonymize_data.log 2>&1",
DESKHUB_CRON_MARKER_END,
];
return implode("\n", $lines);
}
function crontab_get_current(): string
{
$out = @shell_exec('crontab -l 2>/dev/null');
return $out !== null ? $out : '';
}
function crontab_is_installed(): bool
{
return str_contains(crontab_get_current(), DESKHUB_CRON_MARKER_START);
}
/** Entfernt einen ggf. vorhandenen DeskHub-Block aus einem Crontab-Text (Hilfsfunktion für install+remove). */
function crontab_strip_marker_block(string $content): string
{
$pattern = '/^' . preg_quote(DESKHUB_CRON_MARKER_START, '/') . '.*?^' . preg_quote(DESKHUB_CRON_MARKER_END, '/') . '\n?/ms';
return preg_replace($pattern, '', $content) ?? $content;
}
/** Schreibt einen kompletten neuen Crontab-Inhalt für den aktuellen Benutzer. */
function crontab_write(string $content): array
{
$descriptors = [0 => ['pipe', 'r'], 1 => ['pipe', 'w'], 2 => ['pipe', 'w']];
$proc = @proc_open('crontab -', $descriptors, $pipes);
if (!is_resource($proc)) {
return ['ok' => false, 'error' => 'crontab-Prozess konnte nicht gestartet werden.'];
}
fwrite($pipes[0], $content);
fclose($pipes[0]);
$stdout = stream_get_contents($pipes[1]); fclose($pipes[1]);
$stderr = stream_get_contents($pipes[2]); fclose($pipes[2]);
$code = proc_close($proc);
if ($code !== 0) {
return ['ok' => false, 'error' => trim($stderr) ?: trim($stdout) ?: "crontab beendete mit Fehlercode $code"];
}
return ['ok' => true, 'error' => ''];
}
/** Installiert/aktualisiert den DeskHub-Block in der Crontab des aktuellen Benutzers. */
function crontab_install(): array
{
$check = crontab_capability_check();
if (!$check['available']) {
return ['ok' => false, 'error' => $check['reason']];
}
$current = crontab_strip_marker_block(crontab_get_current());
$current = rtrim($current, "\n");
$new = ($current !== '' ? $current . "\n\n" : '') . crontab_marker_block() . "\n";
return crontab_write($new);
}
/** Entfernt den DeskHub-Block aus der Crontab, lässt alle anderen Einträge unangetastet. */
function crontab_uninstall(): array
{
$check = crontab_capability_check();
if (!$check['available']) {
return ['ok' => false, 'error' => $check['reason']];
}
$current = crontab_get_current();
if (!str_contains($current, DESKHUB_CRON_MARKER_START)) {
return ['ok' => true, 'error' => '']; // war gar nicht installiert
}
$new = crontab_strip_marker_block($current);
return crontab_write($new);
}