====== std.inotify ======
Dateisystem-Ereignisüberwachung via Linux inotify (WP-10). Meldet Datei- und Verzeichnisänderungen ohne Polling: Ein inotify-fd liefert via ''InotifyRead'' eine Folge von ''inotify_event''-Einträgen. Mehrere Pfade können über separate Watch-Deskriptoren (''wd'') an einen einzigen fd gehängt werden.
→ [[lyx_-_programmiersprache:units|Standard Library]] · [[lyx_-_programmiersprache:units:fs|std.fs]] · [[lyx_-_programmiersprache:units:fs_ext|std.fs_ext]]
----
===== Event-Masken =====
^ Konstante ^ Wert ^ Beschreibung ^
| ''IN_ACCESS'' | 1 | Datei wurde gelesen |
| ''IN_MODIFY'' | 2 | Dateiinhalt wurde geändert |
| ''IN_ATTRIB'' | 4 | Metadaten geändert (Rechte, Timestamps, …) |
| ''IN_CLOSE_WRITE'' | 8 | Zum Schreiben geöffnete Datei wurde geschlossen |
| ''IN_CLOSE_NOWRITE'' | 16 | Zum Lesen geöffnete Datei wurde geschlossen |
| ''IN_OPEN'' | 32 | Datei oder Verzeichnis wurde geöffnet |
| ''IN_MOVED_FROM'' | 64 | Datei aus überwachtem Verzeichnis verschoben |
| ''IN_MOVED_TO'' | 128 | Datei in überwachtes Verzeichnis verschoben |
| ''IN_CREATE'' | 256 | Datei oder Verzeichnis erstellt |
| ''IN_DELETE'' | 512 | Datei oder Verzeichnis gelöscht |
| ''IN_DELETE_SELF'' | 1024 | Überwachtes Objekt selbst wurde gelöscht |
| ''IN_MOVE_SELF'' | 2048 | Überwachtes Objekt selbst wurde verschoben |
| ''IN_ALL_EVENTS'' | 4095 | Alle obigen Ereignisse (0xFFF) |
| ''IN_UNMOUNT'' | 8192 | Dateisystem wurde ausgehängt |
| ''IN_Q_OVERFLOW'' | 16384 | Kernel-Event-Queue übergelaufen |
| ''IN_IGNORED'' | 32768 | Watch wurde automatisch entfernt |
| ''IN_ISDIR'' | 1073741824 | Ereignis betrifft ein Verzeichnis |
| ''IN_ONESHOT'' | −2147483648 | Nur ein Event, danach Watch automatisch entfernen |
===== Init-Flags =====
^ Konstante ^ Wert ^ Beschreibung ^
| ''IN_NONBLOCK'' | 2048 | Nicht-blockierendes ''InotifyRead'' (''O_NONBLOCK'') |
| ''IN_CLOEXEC'' | 524288 | fd bei exec() schließen (''O_CLOEXEC'') |
===== inotify_event-Struktur =====
Jedes von ''InotifyRead'' gelieferte Ereignis hat folgenden Aufbau im Puffer:
^ Offset ^ Typ ^ Feld ^ Beschreibung ^
| +0 | int32 | ''wd'' | Watch-Deskriptor |
| +4 | uint32 | ''mask'' | Ereignistyp (IN_*-Konstante) |
| +8 | uint32 | ''cookie'' | Bewegungs-Cookie (IN_MOVED_FROM/TO-Korrelation) |
| +12 | uint32 | ''len'' | Länge von ''name[]'' inkl. Null-Byte und Padding |
| +16 | char[] | ''name'' | Dateiname, null-terminiert, auf 4 Bytes aufgefüllt |
''INOTIFY_EVENT_HDR_SIZE'' (16) ist die feste Header-Größe ohne ''name''.
----
===== Funktionen =====
==== fd erstellen und verwalten ====
^ Signatur ^ Beschreibung ^
| ''InotifyCreate(): int64'' | Erstellt eine neue inotify-Instanz mit ''IN_CLOEXEC''; gibt fd zurück, < 0 bei Fehler |
| ''InotifyWatch(fd: int64, path: pchar, mask: int64): int64'' | Überwacht ''path'' mit den angegebenen Masken; gibt Watch-Deskriptor ''wd'' (> 0) zurück |
| ''InotifyUnwatch(fd: int64, wd: int64): int64'' | Entfernt einen Watch-Deskriptor; gibt 0 bei Erfolg |
==== Events lesen ====
^ Signatur ^ Beschreibung ^
| ''InotifyRead(fd: int64, buf: int64, bufSize: int64): int64'' | Liest Events in ''buf''; gibt gelesene Bytes zurück. ''buf'' muss mindestens ''INOTIFY_EVENT_HDR_SIZE + max_name_len'' Bytes groß sein |
==== inotify_event-Felder auslesen ====
^ Signatur ^ Beschreibung ^
| ''InotifyEventWd(buf: int64): int64'' | Watch-Deskriptor des Events (int32 an Offset 0) |
| ''InotifyEventMask(buf: int64): int64'' | Ereignismaske des Events (uint32 an Offset 4) |
| ''InotifyEventCookie(buf: int64): int64'' | Bewegungs-Cookie (uint32 an Offset 8); korreliert IN_MOVED_FROM mit IN_MOVED_TO |
| ''InotifyEventLen(buf: int64): int64'' | Länge des ''name''-Felds inkl. Padding (uint32 an Offset 12) |
| ''InotifyEventName(buf: int64): int64'' | Zeiger auf das ''name''-Feld (Offset 16); null-terminierter Dateiname |
| ''InotifyEventNext(buf: int64): int64'' | Zeiger auf das nächste Event im Puffer (''buf + HDR_SIZE + len'') |
----
===== Verwendung =====
==== Einzelnes Verzeichnis überwachen ====
import std.inotify;
import std.alloc;
import std.io;
fn WatchDir(path: pchar): void {
var fd := InotifyCreate();
var wd := InotifyWatch(fd, path, IN_CREATE | IN_DELETE | IN_MODIFY);
var buf: int64 := alloc(4096);
var running: bool := true;
while (running) {
var n := InotifyRead(fd, buf, 4096);
if (n < 0) { running := false; }
var ptr := buf;
while (ptr < buf + n) {
var mask := InotifyEventMask(ptr);
var name := InotifyEventName(ptr);
if ((mask & IN_CREATE) != 0) {
PrintLn("Erstellt: " + name);
}
if ((mask & IN_DELETE) != 0) {
PrintLn("Gelöscht: " + name);
}
if ((mask & IN_MODIFY) != 0) {
PrintLn("Geändert: " + name);
}
if ((mask & IN_ISDIR) != 0) {
PrintLn(" (Verzeichnis)");
}
ptr := InotifyEventNext(ptr);
}
}
free(buf, 4096);
InotifyUnwatch(fd, wd);
close(fd);
}
==== Mehrere Pfade mit einem fd überwachen ====
import std.inotify;
import std.alloc;
import std.io;
fn WatchMultiple(): void {
var fd := InotifyCreate();
// Verschiedene Verzeichnisse mit unterschiedlichen Masken
var wd1 := InotifyWatch(fd, "/etc", IN_MODIFY | IN_CREATE);
var wd2 := InotifyWatch(fd, "/var/log", IN_MODIFY);
var wd3 := InotifyWatch(fd, "/tmp", IN_ALL_EVENTS);
var buf: int64 := alloc(8192);
var n := InotifyRead(fd, buf, 8192);
var ptr := buf;
while (ptr < buf + n) {
var wd := InotifyEventWd(ptr);
var mask := InotifyEventMask(ptr);
var name := InotifyEventName(ptr);
PrintLn("wd=" + IntToStr(wd) + " mask=" + IntToStr(mask) + " name=" + name);
ptr := InotifyEventNext(ptr);
}
free(buf, 8192);
close(fd);
}
==== IN_ONESHOT: Einmaliges Event ====
import std.inotify;
import std.alloc;
// Wartet auf die erste Änderung einer Datei, dann Schluss.
fn WaitForChange(path: pchar): void {
var fd := InotifyCreate();
InotifyWatch(fd, path, IN_MODIFY | IN_ONESHOT);
var buf: int64 := alloc(128);
InotifyRead(fd, buf, 128); // blockiert bis zum ersten Event
free(buf, 128);
close(fd);
}
==== Bewegungs-Cookie: verschobene Dateien korrelieren ====
''IN_MOVED_FROM'' und ''IN_MOVED_TO'' haben denselben ''cookie''-Wert und können so als zusammengehörendes Rename/Move-Paar erkannt werden.
import std.inotify;
import std.alloc;
import std.io;
fn TrackMoves(dir: pchar): void {
var fd := InotifyCreate();
InotifyWatch(fd, dir, IN_MOVED_FROM | IN_MOVED_TO);
var buf: int64 := alloc(4096);
var n := InotifyRead(fd, buf, 4096);
var ptr := buf;
while (ptr < buf + n) {
var mask := InotifyEventMask(ptr);
var cookie := InotifyEventCookie(ptr);
var name := InotifyEventName(ptr);
if ((mask & IN_MOVED_FROM) != 0) {
PrintLn("Quelle (cookie=" + IntToStr(cookie) + "): " + name);
}
if ((mask & IN_MOVED_TO) != 0) {
PrintLn("Ziel (cookie=" + IntToStr(cookie) + "): " + name);
}
ptr := InotifyEventNext(ptr);
}
free(buf, 4096);
close(fd);
}
----
===== Hinweise =====
* Der inotify-fd kann zusammen mit ''std.net.epoll'' in eine epoll-Instanz eingehängt werden, um Dateisystemereignisse und Netzwerkereignisse in derselben Event-Loop zu verarbeiten.
* Bei rekursiver Verzeichnisüberwachung muss jedes Unterverzeichnis einzeln per ''InotifyWatch'' registriert werden — inotify ist nicht rekursiv.
* ''IN_Q_OVERFLOW'' signalisiert, dass Events verloren gingen. Puffergröße erhöhen oder Lese-Rate steigern.
* ''InotifyEventLen'' kann 0 sein, wenn das Ereignis kein ''name''-Feld hat (z. B. ''IN_DELETE_SELF'').
----
===== Verwandte Units =====
* ''[[lyx_-_programmiersprache:units:fs|std.fs]]'' — Datei-I/O
* ''[[lyx_-_programmiersprache:units:fs_ext|std.fs_ext]]'' — Erweitertes Datei-I/O (pread, sendfile …)
* ''[[lyx_-_programmiersprache:units:net:epoll|std.net.epoll]]'' — Event-Loop-Integration
* ''[[lyx_-_programmiersprache:units:signals|std.signals]]'' — UNIX-Signalbehandlung
Letzte Aktualisierung: 2026-06-05