User Tools

Site Tools


k19:k19.4:start

19.4 Component gb.inotify

With the component gb.inotify by Tobias Boege you can access the linux-specific interface “inotify” in Gambas programs. Information about this interface can be found at https://wiki.ubuntuusers.de/inotify. The interface allows you to intercept selected file system events. You can thus monitor a path of the file system behind which a file or directory may be located.

However, when using the component, please note the following:

  • It does not specify how long apart the occurrence of a file system event and the occurrence of the corresponding Gambas event can be.
  • For example, if a file is created, the Gambas process may a priori not know about it until an hour later. By that time, however, the file may have been deleted. This is in the nature of things, because file system events are asynchronous.
  • Under normal operating load on the computer, however, the kernel makes an effort to send the events promptly.
  • A Gambas program using the Watch single class of the gb.inotify component is notified by the Linux kernel of activity in the watched directory, where Gambas events of the Watch object are evaluated.

19.4.1 Class Watch

The Watch class (gb.inotify) represents a file system object to be monitored. You can create the class. The class behaves like a static array whose values can only be read. Watching a directory is not recursive for its subdirectories. This means that you only get events from the directory itself and for its immediate entries. However, you can create a separate Watch object for each subdirectory of a base directory and even use the Create event to monitor subdirectories created at runtime.

19.4.2 Creating a Watch object

You can create Watch objects regularly using the New instruction. The signature of the constructor is as follows:

  hWatch = New Watch ( Path As String [ , NoFollowLink As Boolean, Events As Integer ] ) As "EventName"

First, the path of the file system entry to be watched is specified. With the optional parameter NoFollowLink you can forbid following symbolic links when interpreting your path. By default, links are followed. The last optional argument Events is a bitmask of events to monitor. Use the constants of the Watch class for this. For example, if you want to monitor the create and delete events of a path sPath, create the Watch object like this:

  hWatch = New Watch(sPath, False, Watch.Create + Watch.Delete) As „MyWatch”

If you do not specify the events mask, the Watch class automatically determines all events for which you have created event handlers under the specified event name. If you have entered

  Private $hWatch As Watch

  Public Sub Form_Open()
    ...
    $hWatch = New Watch(sPath) As "MyWatch"
  End

  Public Sub MyWatch_Create()
    ...
  End

  Public Sub MyWatch_Delete()
    ...
  End

the Watch class recognises that you want to intercept Create and Delete events from MyWatch and sets these - and only these - events to receivable. For performance reasons, you should never specify more events in the Events mask than you need. You can later change the receivable events via the Events property of the Watch class. You use this property like an array of Boolean values and index them using the Watch constants:

  $hWatch.Events[Watch.Create] = False  ' Create-Events sind nun nicht mehr empfangbar
  $hWatch.Events[Watch.Move] = True     ' Stattdessen interessiert nun das Move-Event

Note that the only job of Watch objects is to trigger events! If you forget to give a Watch object an event name, it cannot trigger events and is useless.

19.4.2.1 Static properties

The Watch class has four static properties that provide additional information for event handlers and should only be used in such. The static properties are used to store data from the kernel during event handler routines.

PropertyData typeDescription
CookieIntegerA cookie is used to link events. This is currently (1.10.2015) only necessary for linking MoveFrom and MoveTo events of the same file.
IsDirBooleanIndicates whether the observation refers or referred to a directory.
NameStringOnly applies to monitored directories: Returns the name of the file or subdirectory from which the event originated. The name is relative to the monitored directory. If the directory itself triggered the event, name = zero. For example, if the directory “abc” is monitored and a file with the file name “xyz” is created there, the create event of “abc” is triggered and Name is set to “xyz”.
UnmountBooleanReturns whether the file system in which the observed path was located was unmounted. If so, the observed object is invalidated immediately after the event is triggered.

Table 19.4.2.1.1 : Static properties of the Watch class

19.4.2.2 Properties

PropertyData typeDescription
Events.Watch.EventsReturns a virtual class to specify the event watch bitmask. This virtual class is used to manage which events are monitored by a particular Watch object.
IsPausedBooleanReturns whether the Watch is currently paused (→ methods Pause and Resume).
PathStringReturns the monitored path.
Tag VariantThe use of this property can be freely determined by the Gambas programmer.

Table 19.4.2.2.1 : Properties of the class Watch

19.4.2.3 Methods

The Watch class has only these two methods:

MethodDescription
PausePauses a Watch object, preventing it from triggering events.
ResumeEnds the pause mode of a Watch object.

Table 19.4.2.3.1 : Methods of the Watch class

19.4.2.4 Events

The Watch class has these events:

EventDescription
CloseThe event is triggered when the monitored file or a file in the monitored directory is closed.
CreateThe event is triggered when an entry (file or directory) is created in the monitored directory.
DeleteThe event is triggered when an entry in the monitored directory or the monitored object itself is deleted.
MoveThe event is triggered when the monitored object has been moved.
MoveFromThe event is triggered when an entry of the monitored directory is moved (event for the source directory of the move).
MoveToThe event is triggered when an entry is moved to the monitored directory (event for the target directory of a move).
OpenThe event is triggered when the monitored file or an entry in the monitored directory is opened.
ReadThe event is triggered when the monitored file or an entry in the monitored directory has been accessed in read-only mode, including execution.
StatThe event is triggered when meta data or file attributes of the monitored object have been changed.
WriteThe event is triggered when the monitored file or an entry in the monitored directory has been write-accessed.

Table 19.4.2.4.1 : Events of the class Watch

As you can see, the meaning and interpretation of an event depends on whether a file or directory is being watched.

19.4.3 Project

The project demonstrates the monitoring of selected temporary directories and a temporary file. The file and the directories are created at runtime and edited (open, modified, moved, deleted) in different ways.

Bild 1 - GUI

Figure 19.4.3.1: Monitoring protocol

The special feature of this project by the author of the component gb.inotify is the triggering of events by an external script. It has to be realised via a task, because on the one hand the monitoring should be as close to real time as possible and on the other hand the task in the project is there to provide for events so that you automatically get to see something when you start the project (→ Task object → Chapter 20.6.0). If the script were executed synchronously, then all editing of the directories and the file would first take place and only later would all monitoring results be received. In the source text you can also see that a separate watch object is created for each new directory and inserted into a watch[ ] array. The corresponding entry in the $aSubDirs array of the type Watch[ ] is removed when the object to be watched has been deleted. If - without a task - the events were only received after the entire script has run through, the created subdirectory would already be deleted when the Gambas process receives its create event. It can therefore no longer be monitored and the process in particular does not receive the events (lying in the past) that have occurred below this subdirectory.

The processing of the directories and the file is done by a separate class → ExternalScript.class.

The source texts are presented in their entirety. The following is the source code in the file ExternalScript.class:

  ' Gambas class file
 
  Inherits Task
 
  Private $sFile As String
  Private $sDir As String
 
  Public Sub _new(sFile As String, sDir As String)
    $sFile = sFile
    $sDir = sDir
  End
 
  Public Sub Main()
  ' &1 ist eine Datei, &2 ist ein Verzeichnis, &3 ist eine Unterverzeichnis in &2
    Shell Subst$("touch &1;  sleep 1; chmod a+w &1; sleep 1;"
    "touch &3;  sleep 1; cat &1; sleep 1;"
    "echo 'test' > &1; sleep 2;"
    "mkdir &2; sleep 1; mv &1 &2; sleep 1;"
    "rm &4; sleep 1; rmdir &2; sleep 1", $sFile, $sDir, File.Dir($sFile), $sDir &/ File.Name($sFile)) Wait
 
    Print "Fertig"
    Flush()
    Do ' Darauf warten, dass der Hauptprozess den Task beendet
      Wait 1
    Loop
  End

Source code FMain.class:

  ' Gambas class file
 
  Private $hTmp As Watch
  Private $aSubdirs As New Watch[]
  Private $hScript As ExternalScript
 
  Public Sub Form_Open()
    Dim sFile As String = Temp$(), sDir As String = Temp$()
 
    FMain.Center
    FMain.Resizable = True
    TextArea1.ReadOnly = True
 
    $hTmp = New Watch(File.Dir(sFile)) As "Tmp" ' Erzeugen eines Watch-Objektes
 
  ' Das Skript muss ein Task sein, weil Sie ja die Events so nah wie möglich zu der Zeit empfangen
  ' wollen, zu der sie ausgelöst werden. Wenn das Skript in diesem Prozess ausgeführt würde,
  ' erhielten Sie die Events gesammelt, nachdem das komplette Skript durchgelaufen ist. So könnten
  ' Sie zum Beispiel keine Ereignisse in den zur Laufzeit erstellten Unterverzeichnissen abfangen.
 
    $hScript = New ExternalScript(sFile, sDir) As "ExternalScript"
    Spinner1.Start()
  End
 
  Public Sub ExternalScript_Read(Data As String)
    If Trim$(Data) <> "Fertig" Then Return
    $hScript.Stop() ' Stoppt den Task als Hintergrund-Prozess
    TextArea1.Insert(gb.NewLine & "*** Externes Skript beendet. ***")
  End
 
  Public Sub Tmp_Read()
    Note("Lesen")
  End ' Tmp_Read()
 
  Public Sub Tmp_Create()
    Dim hSubdir As Watch
 
    If Watch.IsDir Then
  ' Das Try steht hier, um eine 'race condition' abzufangen: Das Unterverzeichnis könnte erstellt und schon
  ' wieder gelöscht worden sein, bevor dieser Event-Handler aufgerufen wurde. Das New Watch(..) könnte
  ' fehlschlagen, weil Sie ein Create-Event bearbeiten, dessen Subjekt schon wieder gelöscht wurde.
      Try hSubdir = New Watch(Last.Path &/ Watch.Name) As "Subdir"
      If Not Error Then
         Note("Neues Unterverzeichnis")
         $aSubdirs.Add(hSubdir)
      Endif
    Else
      Note("Erstellen", "initialer Modus " & Stat(Last.Path &/ Watch.Name).Auth)
    Endif
  End
 
  Public Sub Tmp_Open()
    Note("Öffnen")
  End
 
  Public Sub Tmp_Close()
    Note("Schließen")
  End
 
  Public Sub Tmp_Write()
    Note("Schreiben")
  End
 
  Public Sub Tmp_Move()
    Note("Umbenennen")
  End
 
  Public Sub Tmp_MoveFrom()
    Note(Subst$("Umbenennen [ Quelle ] (Cookie &1)", Watch.Cookie))
  End
 
  Public Sub Subdir_MoveTo()
    Note(Subst$("Umbenennen [ Ziel ] (Cookie &1)", Watch.Cookie))
  End
 
  Public Sub Tmp_Delete()
    Note("Löschen")
  End
 
  Public Sub Subdir_Delete()
    Note(Subst$("Löschen &1Unterverzeichnis", IIf(Watch.Name, "in ", "")))
    If Not Watch.Name Then $aSubdirs.Remove($aSubdirs.Find(Last))
  End
 
  Public Sub Tmp_Stat()
    With Stat(Last.Path &/ Watch.Name)
      Note("Datenabfrage", Subst$("Modus &1, letzter Zugriff &2", .Auth, .LastAccess))
    End With
    Catch
      ' Last.Path &/ Watch.Name könnte nicht mehr existieren und Stat könnte deshalb fehlschlagen.
  End
 
  Private Sub Note(sWhat As String, …) ' Information: http://gambaswiki.org/wiki/comp/gb/param
    Dim sArg As String
 
    TextArea1.Insert(sWhat & "       " & Last.Path &/ Watch.Name)
    For Each sArg In Param
        TextArea1.Insert(" " & sArg)
    Next
    TextArea1.Insert(gb.NewLine)
    TextArea1.Pos = Len(TextArea1.Text)
  End
 
  Public Sub btnClose_Click()
    FMain.Close()
  End
 
  Public Sub Form_Close()
    If $hScript.Running Then $hScript.Stop
    $hTmp = Null
    $aSubdirs.Clear()
    Spinner1.Stop()
    Wait 1
  End

All monitored events are displayed when triggered by the script → Figure 19.4.3.1 Monitoring log.

Download

The website uses a temporary session cookie. This technically necessary cookie is deleted when the browser is closed. You can find information on cookies in our privacy policy.
k19/k19.4/start.txt · Last modified: 14.10.2023 by emma

Page Tools