====== 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