# tcl-inotify

NAME
SYNOPSIS
DESCRIPTION
EXAMPLE
OUTPUT
EXAMPLE
AUTHOR
COPYRIGHT

## NAME

| tcl−inotify − File−system notification service for Tcl

## SYNOPSIS

| package require inotify inotify create watch handler inotify info watch add pathname flags watch del pathname watch queue watch read watch info rename watch {}

## DESCRIPTION

| This is a Tcl extension that provides an interface to the inotify file system notification service of the Linux kernel. Inotify is a Linux kernel subsystem that provides file system event notifications. It was written by John McCutchan with help from Robert Love and later Amy Griffis to replace dnotify. It was included in the mainline kernel from release 2.6.13, and could be compiled into 2.6.12 and possibly earlier releases by use of a patch. Its function is essentially an extension to filesystems to notice changes to the filesystem, and report those changes to applications. inotify create watch handler Creates an inotify instance in the form of a new Tcl command, named watch. Whenever there are events for this instance, handler procedure is executed with a single argument, the instanceId. On success the command returns instanceId, otherwise an error is generated that needs to be catched. inotify info It returns a list of length n * 3, where n is the number of active instances. Every triplet is comprised by: instanceId handler watch. The command can generate errors that need to be catched. watch add pathname flags Adds a watch to the inotify instance that corresponds to watch command. The new watch monitors the pathname object (which can be a file or a directory) for the events specified by flags. On success the command returns watchId, otherwise an error is generated that needs to be catched. watch del pathname Removes the watch associated with pathname.The command can generate errors that need to be catched. watch queue Returns the bytes in the kernel buffer that are occupied by inotify events which haven’t been processed yet (pending). watch read Returns a list of dictionaries, one for each pending event for the inotify instance that corresponds to the watch command. The format of the dictionary is: watchid idVal flags flagsVal cookie cookieVal filename nameVal Up to 32 pending events can be returned with a single invocation of ’read’ subcommand. Always use the ’queue’ subcommand to find out if there are more events to be retrieved. watch info It returns a list of length n * 3, where n is the number of active instances. Every triplet is comprised by: pathname watchId flags. The command can generate errors that need to be catched. rename watch {} Removes all the instance that corresponds to the watch command, and all associated watches. PARAMETERS instanceId is a number that uniquely identifies a set of watches that are handled by the same handler procedure. watchId is a number that uniquely identifies a watch. handler is a procedure (with optional namespace), that is executed every time there are events in the kernel event queue for the corresponding instanceId. The events can be read in batch by the handler procedure by invoking watch read. The instanceId is provided as an argument to the handler. cookie is used to associate two different events, one IN_MOVED_FROM and one IN_MOVED_TO that involve the same file/directory. flags is a string composed of single character event identifiers. The following events are supported:
        Id Name              Description
         n IN_CREATE         File was created.
         r IN_ACCESS         File was read from.
         w IN_MODIFY         File was written to.
         a IN_ATTRIB         File’s metadata (inode or xattr) was changed.
         C IN_CLOSE_WRITE    File was closed (and was open for writing).
         c IN_CLOSE_NOWRITE  File was closed (and was not open for writing).
         o IN_OPEN           File was opened.
         m IN_MOVED_FROM     File was moved away from watch.
         M IN_MOVED_TO       File was moved to watch.
         d IN_DELETE         File was deleted.
         s IN_DELETE_SELF    The watch itself was deleted.

    In addition to the above, for event registration only, the following are also supported: (only for the watch add command)

        Id Name              Description
         1 IN_ONESHOT        The watch will be automatically removed during
                             generation of the first event.
         _ IN_CLOSE          IN_CLOSE_WRITE | IN_CLOSE_NOWRITE
         > IN_MOVE           IN_MOVED_FROM | IN_MOVED_TO
         * IN_ALL_EVENTS     Bitwise OR of all events.

    In a similar manner, the following are specific for event reporting: (only for the watch read command)

        Id Name              Description
         u IN_UNMOUNT        The backing filesystem was unmounted.
         f IN_Q_OVERFLOW     The inotify queue overflowed.
         i IN_IGNORED        The watch was automatically removed, because the
                             file was deleted or its filesystem was unmounted.
         D IN_ISDIR          The event occurred against a directory.

## EXAMPLE

    # Monitor a directory for all events, and cause some to trigger
    package require inotify

    set num 0
    proc handler { fd } {
        variable term
        variable num
        puts "____________________________________________"
        puts "Invocation:$num"
        puts "Instance: $fd"
        puts "Remaining:[watch queue]"
        puts "Inotify Info:[inotify info]"
        puts "Watch Info:[watch info]"
        set events [watch read]
        puts "Events (raw):$events0

       # In single−threaded scripts, reading this buffer always gives the data that caused to invoke the handler
        foreach {watchId flags cookie filepath} $events {
          puts "Watch:$watchId"
          puts "Flags:$flags"
          puts "Cookie:$cookie"
          puts "Object:$filepath0
        }
        incr num
        if {$num >= 5} {
            set term 1
        }
    }

    set fd [inotify create "watch" "::handler"]
    puts Created

    set wd [watch add [pwd] {*}]
    puts Added

    watch remove [pwd]
    puts Removed

    set wd [watch add [pwd] {*}]
    puts Added

    after 1000 [list exec echo a > test.txt]
    after 2000 [list exec mv test.txt delme.txt]
    after 3000 [list exec rm delme.txt]
    after 4000 [list exec ls]
    after 5000 [list set term 1]

    set term 0
    puts "waiting.."
    vwait term

    puts "0xiting.."
    rename watch {}
    rename inotify {}

## OUTPUT

    Created
    Added
    Removed
    Added
    waiting..
    ____________________________________________
    Invocation:     0
    Instance:       5
    Remaining:      0
    Inotify Info:   5 ::handler watch
    Watch Info:     /tmp/inotify 2 *
    Events (raw):   1 i 0 {}

           Watch:  1
            Flags:  i
            Cookie: 0
            Object:

    ____________________________________________
    Invocation:     1
    Instance:       5
    Remaining:      0
    Inotify Info:   5 ::handler watch
    Watch Info:     /tmp/inotify 2 *
    Events (raw):   2 n 0 test.txt 2 o 0 test.txt 2 w 0 test.txt 2 C 0 test.txt

           Watch:  2
            Flags:  n
            Cookie: 0
            Object: test.txt

           Watch:  2
            Flags:  o
            Cookie: 0
            Object: test.txt

           Watch:  2
            Flags:  w
            Cookie: 0
            Object: test.txt

           Watch:  2
            Flags:  C
            Cookie: 0
            Object: test.txt

    ____________________________________________
    Invocation:     2
    Instance:       5
    Remaining:      0
    Inotify Info:   5 ::handler watch
    Watch Info:     /tmp/inotify 2 *
    Events (raw):   2 m 585 test.txt 2 M 585 delme.txt

           Watch:  2
            Flags:  m
            Cookie: 585
            Object: test.txt

           Watch:  2
            Flags:  M
            Cookie: 585
            Object: delme.txt

    ____________________________________________
    Invocation:     3
    Instance:       5
    Remaining:      0
    Inotify Info:   5 ::handler watch
    Watch Info:     /tmp/inotify 2 *
    Events (raw):   2 d 0 delme.txt

           Watch:  2
            Flags:  d
            Cookie: 0
            Object: delme.txt

    ____________________________________________
    Invocation:     4
    Instance:       5
    Remaining:      0
    Inotify Info:   5 ::handler watch
    Watch Info:     /tmp/inotify 2 *
    Events (raw):   2 oD 0 {} 2 cD 0 {}

           Watch:  2
            Flags:  oD
            Cookie: 0
            Object:

           Watch:  2
            Flags:  cD
            Cookie: 0
            Object:

    exiting..

## EXAMPLE

    # An efficient tailf implementation using inotify
    package require inotify
    package provide tailf

    namespace eval tailf {
    namespace export add rem

    array set File2wd {}
    array set Wd2info {}

    inotify create ::tailf::watch ::tailf::handler

    proc getpos {filepath} {
        set fd [open $filepath r]
        seek $fd 0 end
        set pos [tell $fd]
        close $fd
        return $pos
    }

    proc handler {args} {
        variable File2wd
        variable Wd2info

       while {[watch queue]} {
            set events [watch read]

           foreach {wd flags cookie filepath} $events {
                if {! [info exists Wd2info($wd)]} {
                    return
                }

               set diff {}
                set callback [lindex $Wd2info($wd) 1]
                set filepath [lindex $Wd2info($wd) 2]
                set old [lindex $Wd2info($wd) 0]
                set new [getpos $filepath]
                set Wd2info($wd) [lreplace $Wd2info($wd) 0 0 $new]

               if {$new < $old} {
                    set old 0
                }

               if {$old != $new} {
                    set fd [open $filepath r]
                    seek $fd $old
                    while {[gets $fd line] >= 0} {
                        append diff "$line" "0
                    }
                    close $fd
                }

               eval [list $callback $filepath $diff]
            }
        }
        return
    }

    proc add {filepath callback} {
        variable File2wd
        variable Wd2info

       if {[file exists $filepath] && [file isfile $filepath]} {
            set pos [getpos $filepath]
            set wd [watch add $filepath {w}]
            set File2wd($filepath) $wd
            set Wd2info($wd) [list $pos $callback $filepath]
        } else {
            error "is either not a file, or does not exist"
        }
    }

    proc rem {filepath} {
        variable File2wd
        variable Wd2info

       if {! [info exists ::tailf::File($filepath)]} {
            error "$filepath is not monitored, or watch has been removed"
        }

       set wd $File2wd($filepath)
        unset Wd2info($wd)
        unset File2wd($filepath)
        watch del $filepath
    }

    } ;# namespace end

    proc puts_hdlr {filepath diff} {
        puts −nonewline $diff
    }

    ::tailf::add "delme.txt" puts_hdlr

    set a {}
    vwait a

## AUTHOR

| Alexandros Stergiakis <sterg@kth.se>

## COPYRIGHT

| Copyright (C) 2008 Alexandros Stergiakis This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>.