Dateiänderungen in einem bestimmten Verzeichnis überwachen

Dateiänderungen in einem bestimmten Verzeichnis überwachen

Bei bestimmten sicherheitsrelevanten Diensten oder älteren Projekten kann es sinnvoll sein Dateiänderungen zu überwachen. Daher habe ich mir die Zeit genommen und ein kleines Skript geschrieben, welches direkt im Homeverzeichnis hinterlegt wird. Im Skript müssen das Unterverzeichnis, Absendermail und Empfänger noch angepasst werden. Es wird bei neuen, gelöschten oder veränderten Dateien eine E-Mail gesendet. Das gilt nur für die Dateien (nicht Ordner) eines bestimmten Ordners (nicht Unterordner). Damit kann gezielt ein bestimmtes Verzeichnis überwacht werden (ohne andauernd E-Mails vom geänderten Cache zu erhalten).

Einfach eine monitor.php und leere file_state.json anlegen und danach in monitor.php eintragen:

<?php

$directory = __DIR__ . '/UNTERORDNER'; // Change to your target directory
$stateFile = __DIR__ . '/file_state.json';
$recipientEmail = '[email protected]';

function getFileStates($dir) {
    $files = scandir($dir);
    $fileStates = [];

    foreach ($files as $file) {
        if ($file === '.' || $file === '..') continue;

        $filePath = $dir . DIRECTORY_SEPARATOR . $file;

        if (is_file($filePath)) {
            $fileStates[$file] = [
                'mtime' => filemtime($filePath),
                'size'  => filesize($filePath)
            ];
        }
    }

    return $fileStates;
}

$previousState = [];
if (file_exists($stateFile)) {
    $json = file_get_contents($stateFile);
    $previousState = json_decode($json, true) ?: [];
}

$currentState = getFileStates($directory);

$added = array_diff_key($currentState, $previousState);
$deleted = array_diff_key($previousState, $currentState);
$modified = [];

foreach ($currentState as $file => $info) {
    if (isset($previousState[$file])) {
        if ($info['mtime'] !== $previousState[$file]['mtime'] || $info['size'] !== $previousState[$file]['size']) {
            $modified[$file] = $info;
        }
    }
}

if (!empty($added) || !empty($deleted) || !empty($modified)) {
    $subject = "File Change Detected in '$directory'";
    $message = "Changes detected in directory: $directory\n\n";

    if (!empty($added)) {
        $message .= "New files:\n" . implode("\n", array_keys($added)) . "\n\n";
    }

    if (!empty($deleted)) {
        $message .= "Deleted files:\n" . implode("\n", array_keys($deleted)) . "\n\n";
    }

    if (!empty($modified)) {
        $message .= "Modified files:\n" . implode("\n", array_keys($modified)) . "\n\n";
    }

    $headers = 'From: [email protected]' . "\r\n";

    mail($recipientEmail, $subject, $message, $headers);
}

file_put_contents($stateFile, json_encode($currentState, JSON_PRETTY_PRINT));

Danach z. B. als stündlichen Cronjob einrichten.

Version für mehrere Ordner

Die nachfolgende Version kann mehrere Ordner gleichzeitig überwachen, die Liste kann beliebig erweitert werden:

<?php
// Configuration
$directories = [
    __DIR__ . '/httpdocs',
    __DIR__ . '/httpdocs/api'
    // Add more directories as needed
];

$stateFile = __DIR__ . '/file_state.json';
$recipientEmail = '[email protected]';
$senderEmail = '[email protected]';

// Get current file states in a directory
function getFileStates($dir) {
    $files = scandir($dir);
    $fileStates = [];

    foreach ($files as $file) {
        if ($file === '.' || $file === '..') continue;

        $filePath = $dir . DIRECTORY_SEPARATOR . $file;

        if (is_file($filePath)) {
            $fileStates[$file] = [
                'mtime' => filemtime($filePath),
                'size'  => filesize($filePath)
            ];
        }
    }

    return $fileStates;
}

// Load previous state
$previousStates = [];
if (file_exists($stateFile)) {
    $json = file_get_contents($stateFile);
    $previousStates = json_decode($json, true) ?: [];
}

// Initialize change log
$allChanges = [];
$currentStates = [];

foreach ($directories as $dir) {
    $prevState = $previousStates[$dir] ?? [];
    $currState = getFileStates($dir);
    $currentStates[$dir] = $currState;

    $added = array_diff_key($currState, $prevState);
    $deleted = array_diff_key($prevState, $currState);
    $modified = [];

    foreach ($currState as $file => $info) {
        if (isset($prevState[$file])) {
            if ($info['mtime'] !== $prevState[$file]['mtime'] || $info['size'] !== $prevState[$file]['size']) {
                $modified[$file] = $info;
            }
        }
    }

    if (!empty($added) || !empty($deleted) || !empty($modified)) {
        $allChanges[$dir] = [
            'added' => array_keys($added),
            'deleted' => array_keys($deleted),
            'modified' => array_keys($modified)
        ];
    }
}

// Save updated state
file_put_contents($stateFile, json_encode($currentStates, JSON_PRETTY_PRINT));

// Send email if any changes
if (!empty($allChanges)) {
    $subject = "File Change Detected in Monitored Directories";
    $message = "The following changes were detected:\n\n";

    foreach ($allChanges as $dir => $changes) {
        $message .= "Directory: $dir\n";

        if (!empty($changes['added'])) {
            $message .= "  New files:\n    - " . implode("\n    - ", $changes['added']) . "\n";
        }

        if (!empty($changes['deleted'])) {
            $message .= "  Deleted files:\n    - " . implode("\n    - ", $changes['deleted']) . "\n";
        }

        if (!empty($changes['modified'])) {
            $message .= "  Modified files:\n    - " . implode("\n    - ", $changes['modified']) . "\n";
        }

        $message .= "\n";
    }

    $headers = "From: $senderEmail\r\n";
    mail($recipientEmail, $subject, $message, $headers);
}

Version für mehrere Ordner und mit Ausschlussliste

<?php
// Configuration
$directories = [
    __DIR__ . '/httpdocs',
    __DIR__ . '/httpdocs/api'
    // Add more directories as needed
];

// Array of files to exclude from change notifications
$excludeFiles = [
    'test.cache',
    'sitemap.xml'
];

$stateFile = __DIR__ . '/file_state.json';
$recipientEmail = '[email protected]';
$senderEmail = '[email protected]';

// Get current file states in a directory
function getFileStates($dir) {
    $files = scandir($dir);
    $fileStates = [];

    foreach ($files as $file) {
        if ($file === '.' || $file === '..') continue;

        $filePath = $dir . DIRECTORY_SEPARATOR . $file;

        if (is_file($filePath)) {
            $fileStates[$file] = [
                'mtime' => filemtime($filePath),
                'size'  => filesize($filePath)
            ];
        }
    }

    return $fileStates;
}

// Load previous state
$previousStates = [];
if (file_exists($stateFile)) {
    $json = file_get_contents($stateFile);
    $previousStates = json_decode($json, true) ?: [];
}

// Initialize change log
$allChanges = [];
$currentStates = [];

foreach ($directories as $dir) {
    $prevState = $previousStates[$dir] ?? [];
    $currState = getFileStates($dir);
    $currentStates[$dir] = $currState;

    $added = array_diff_key($currState, $prevState);
    $deleted = array_diff_key($prevState, $currState);
    $modified = [];

    foreach ($currState as $file => $info) {
        if (isset($prevState[$file])) {
            if ($info['mtime'] !== $prevState[$file]['mtime'] || $info['size'] !== $prevState[$file]['size']) {
                $modified[$file] = $info;
            }
        }
    }

    if (!empty($added) || !empty($deleted) || !empty($modified)) {
        $allChanges[$dir] = [
            'added' => array_keys($added),
            'deleted' => array_keys($deleted),
            'modified' => array_keys($modified)
        ];
    }
}

// Save updated state
file_put_contents($stateFile, json_encode($currentStates, JSON_PRETTY_PRINT));

// Check if changes only involve excluded files
$onlyExcludedFilesChanged = true;
foreach ($allChanges as $dir => $changes) {
    foreach (['added', 'deleted', 'modified'] as $changeType) {
        if (!empty($changes[$changeType])) {
            foreach ($changes[$changeType] as $file) {
                if (!in_array($file, $excludeFiles)) {
                    $onlyExcludedFilesChanged = false;
                    break 2; // Break out of both loops
                }
            }
        }
    }
}

// Send email if any changes, but only if non-excluded files changed
if (!empty($allChanges) && !$onlyExcludedFilesChanged) {
    $subject = "File Change Detected in Monitored Directories";
    $message = "The following changes were detected:\n\n";

    foreach ($allChanges as $dir => $changes) {
        $message .= "Directory: $dir\n";

        if (!empty($changes['added'])) {
            $message .= "  New files:\n    - " . implode("\n    - ", $changes['added']) . "\n";
        }

        if (!empty($changes['deleted'])) {
            $message .= "  Deleted files:\n    - " . implode("\n    - ", $changes['deleted']) . "\n";
        }

        if (!empty($changes['modified'])) {
            $message .= "  Modified files:\n    - " . implode("\n    - ", $changes['modified']) . "\n";
        }

        $message .= "\n";
    }

    $headers = "From: $senderEmail\r\n";
     mail($recipientEmail, $subject, $message, $headers);
}
?>

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert