Table of Contents

6.8 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:

6.8.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 monitor subdirectories created at runtime with the create event.

6.8.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 are now no longer receivable
$hWatch.Events[Watch.Move] = True ' Instead, the Move event is now of interest

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.

6.8.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 (12 July 2018) 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 6.8.2.1.1 : Static properties of the Watch class

6.8.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 6.8.2.2.1 : Properties of the class Watch

6.8.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 6.8.2.3.1 : Methods of the Watch class

6.8.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 a read-only manner, 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 6.8.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.

6.8.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 in different ways (open, change, move, delete).

B1
Figure 6.8.3.1: Monitoring protocol

The special feature of this project is the triggering of events by an external script. It must be realised via a task, because on the one hand the monitoring should take place 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 in chapter 20.6.0). If the script were to be 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. Thus, it can no longer be monitored and, in particular, the process does not receive the (past) events that occurred below this subdirectory.

B2
Figure 6.8.3.2: Monitoring log - end

The processing of the directories and the file is done by the own class in ExternalScript.class.

The source code for the presented project is given in full. The source code of the class ExternalScript.class follows:

' 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()
  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 "Finished"
  Flush
  Do ' Wait for the main process to finish the task
    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$()
  Dim sDir As String = Temp$()
 
  FMain.Resizable = False
  TextArea1.ReadOnly = True
  $hTmp = New Watch(File.Dir(sFile)) As "Tmp"
' The script must be a task because the events are as close as possible to each other.
' at the time they are triggered. If the script would be executed in this process,
' you received the events after the complete script has run. So could For example,
' you cannot intercept events in the subdirectories created at runtime.
  $hScript = New ExternalScript(sFile, sDir) As "ExternalScript"
  Spinner1.Start()
End
 
Public Sub ExternalScript_Read(Data As String)
  If Trim$(Data) <> "Finished" Then Return
  $hScript.Stop() ' Stops the task as a background process
  TextArea1.Insert(gb.NewLine & "● ● ● External script terminated. ● ● ●")
  TextArea1.Background = &HF5FFE6
  Spinner1.Stop()
  Spinner1.Visible = False
End
 
Public Sub Tmp_Read()
  Note("Lesen")
End
 
Public Sub Tmp_Create()
 
  Dim hSubdir As Watch
 
  If Watch.IsDir Then
' The following try is necessary here to intercept a 'race condition':
' The Subdirectory could have been created and deleted before this
' event handler has been called. The New Watch(..) could fail because
' edit a Create event whose subject has already been deleted.
     Try hSubdir = New Watch(Last.Path &/ Watch.Name) As "Subdir"
     If Not Error Then
        Note("New subdirectory")
        $aSubdirs.Add(hSubdir)
     Endif
  Else
     Note("Create", "initial mode" & Stat(Last.Path &/ Watch.Name).Auth)
  Endif
End
 
Public Sub Tmp_Open()
  Note("Open")
End
 
Public Sub Tmp_Close()
  Note("Close")
End
 
Public Sub Tmp_Write()
  Note("Write")
End
 
Public Sub Tmp_Move()
  Note("Rename")
End
 
Public Sub Tmp_MoveFrom()
  Note("Rename", "[Source]", Subst$(" (Cookie &1)", Watch.Cookie))
End
 
Public Sub Subdir_MoveTo()
  Note("Rename", "[Destination]", Subst$(" (Cookie &1)", Watch.Cookie))
End
 
Public Sub Tmp_Delete()
  Note("Delete")
End
 
Public Sub Subdir_Delete()
  Note(Subst$("Delete &1subdirectory", 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("Data query", Subst$("Modus &1, last accsess &2", .Auth, .LastAccess))
  End With
  Catch
' Last.Path &/ Watch.Name might no longer exist and Stat might fail.
End
 
' The three dots indicate that the Note() procedure has a variable argument list.
' After the mandatory parameter sWhat, k >= 0 other arguments of any type can follow.
' Read more about this at http://gambaswiki.org/wiki/lang/methoddecl and
' http://gambaswiki.org/wiki/comp/gb/param
Private Sub Note(sWhat As String, ...)
 
  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)
  Print Param.Count
End
 
Public Sub btnClose_Click()
  FMain.Close
End
 
Public Sub Form_Close()
  If $hScript.Running Then $hScript.Stop
  $hTmp = Null
  $aSubdirs.Clear()
  Wait 1
End

All monitored events are displayed when they are triggered by the script, as you can see in the monitoring log in Figure 6.8.3.2.

Download

Project

Download