Solaris File Event Notification

Suppose you want your Solaris shell script to take some kind of action when a new file arrives in a certain directory. Traditionally this has been done by periodically polling the directory. Not very elegant and what is worse: it uses unnecessary resources.

Linux has the inotify kernel subsystem and on many distros you’ll also find the inotify-tools so that you can use this from the command line and thus in a script. You would typically use the inotifywait command in scripts to cause a blocking wait for some file event to happen.

So what about Solaris ?

The equivalent of Linux’s inotify in Solaris is called File Events Notifications but the caveat is that although it was created in 2007 it didn’t make it into Solaris until version 11. So you’ll need Solaris v11.0 or Solaris v11.1 or later.

In Solaris 11.x there’s is no equivalent to the inotify-tools but it is extremely easy to create so this is what I’ll do here.

I’ve created a small tool that I call watchdir (see source below). It can be used as a command in a script and takes two arguments namely the name of the directory to watch and optionally a timeout. Feel free to steal it and elaborate on it.

Example:

watchdir /foo/bar

will wait indefinitely for changes to the /foo/bar directory. When a change happens the command will return. See return codes in source code below.

Compile and build

This is the easy part. All you have to do is this:

First take the C source code that you find on this page and put it into a new file called watchdir.c.

(if using GNU C/C++ Compiler)

pkg install pkg://solaris/developer/gcc-45
pkg install pkg://solaris/system/header
gcc  watchdir.c -o watchdir

or

(if using Solaris Studio Compiler)

# Install steps for Solaris Studio compiler not shown here
cc  watchdir.c -o watchdir

After this you now have a binary called ‘watchdir’ that you can use in scripts, etc. It is pretty self-explanatory. Have a look at the header of the source code which explains how to use it.

Source code

Example usage

Here’s a Bash example of how watchdir can potentially be used from within a shell script.

To do

There are many extensions that can be done from this and certainly watchdir is nowhere as flexible as the inotifywait command in Linux. Having said that it is more than sufficient for my purpose. I haven’t actually had a need for something more advanced.

It currently triggers on FILE_MODIFIED or FILE_ATTRIB (source code line 207). In this case remember that ‘file’ is really a directory, because that is what we’re watching. If somebody puts a new file into the directory or deletes a file from the directory this will modify the directory and since we’re subscribing to ‘MODIFIED’ events we’ll get a callback. The reason for also using FILE_ATTRIB eludes me (it is some months since I wrote this) but I guess the thinking was that I also wanted to be notified if somebody made the directory read-only .. or something. You can play with this and find the combination that fits your purpose. Or you could change the source code and have such options as part of the command line input.

Credit

The source code is highly inspired / stolen from Prakash Sangappa’s blog. I’ve cleaned it up, made some more source code comments and most of all I’ve made it operational as a command line tool. Feel free to grab it, steal it, etc. And Oracle if you’re listening: I would like to see something like this become part of core Solaris.

Advertisements
Posted in Uncategorized
31 comments on “Solaris File Event Notification
  1. I tried to compile it under Solaris 11.1 with gcc like you described, but the compiler fails with a lot of errors and warnings.

    • petersblogwp says:

      @philipp. I just did it again to make sure. No probs. Feel free to post the errors you’re seeing when you execute gcc.

      • ./watchdir.c:74:9: error: #include expects “FILENAME” or
        ./watchdir.c:75:9: error: #include expects “FILENAME” or
        ./watchdir.c:78:9: error: #include expects “FILENAME” or
        ./watchdir.c:79:9: error: #include expects “FILENAME” or
        ./watchdir.c:80:9: error: #include expects “FILENAME” or
        ./watchdir.c:81:9: error: #include expects “FILENAME” or
        ./watchdir.c:82:9: error: #include expects “FILENAME” or
        ./watchdir.c:85:25: error: field ‘fobj’ has incomplete type
        ./watchdir.c: In function ‘process_event’:
        ./watchdir.c:104:27: error: ‘FILE_EXCEPTION’ undeclared (first use in this function)
        ./watchdir.c:104:27: note: each undeclared identifier is reported only once for each function it appears in
        ./watchdir.c: In function ‘print_syntax’:
        ./watchdir.c:119:3: warning: incompatible implicit declaration of built-in function ‘fprintf’
        ./watchdir.c:119:11: error: ‘stdout’ undeclared (first use in this function)
        ./watchdir.c: In function ‘is_integer’:
        ./watchdir.c:140:25: warning: incompatible implicit declaration of built-in function ‘strlen’
        ./watchdir.c: In function ‘main’:
        ./watchdir.c:153:9: error: ‘port_event_t’ undeclared (first use in this function)
        ./watchdir.c:153:22: error: expected ‘;’ before ‘pe’
        ./watchdir.c:159:28: warning: incompatible implicit declaration of built-in function ‘fprintf’
        ./watchdir.c:159:36: error: ‘stderr’ undeclared (first use in this function)
        ./watchdir.c:166:17: warning: incompatible implicit declaration of built-in function ‘fprintf’
        ./watchdir.c:179:16: warning: incompatible implicit declaration of built-in function ‘malloc’
        ./watchdir.c:185:35: warning: incompatible implicit declaration of built-in function ‘strdup’
        ./watchdir.c:187:13: warning: incompatible implicit declaration of built-in function ‘free’
        ./watchdir.c:193:13: warning: incompatible implicit declaration of built-in function ‘fprintf’
        ./watchdir.c:194:13: warning: incompatible implicit declaration of built-in function ‘free’
        ./watchdir.c:200:13: warning: incompatible implicit declaration of built-in function ‘fprintf’
        ./watchdir.c:207:24: error: ‘FILE_MODIFIED’ undeclared (first use in this function)
        ./watchdir.c:207:38: error: ‘FILE_ATTRIB’ undeclared (first use in this function)
        ./watchdir.c:217:34: error: ‘PORT_SOURCE_FILE’ undeclared (first use in this function)
        ./watchdir.c:224:17: warning: incompatible implicit declaration of built-in function ‘fprintf’
        ./watchdir.c:225:45: error: ‘errno’ undeclared (first use in this function)
        ./watchdir.c:226:17: warning: incompatible implicit declaration of built-in function ‘free’
        ./watchdir.c:234:30: error: ‘pe’ undeclared (first use in this function)
        ./watchdir.c:252:30: error: ‘ETIME’ undeclared (first use in this function)

      • petersblogwp says:

        See reply elsewhere on this page. The source code listing on the blog post has been updated.

  2. Even with Oracle Solaris Studio 12.3: cc: acomp failed for watchdir.c

    • petersblogwp says:

      @philipp. Apologies. This was a some kind of cut’n paste error into WordPress where WordPress had stolen “<” and “>” from the text, but only some of them (lines 74-82 in source source code). I cannot really explain why but I have now reposted the source code and this time I’ve done a proper check, i.e. I’ve copied the text from the Blog page into vi, saved it and tried to compile it. That works for me.

  3. Yes, it works now! I compiled it with openindiana and gcc-3. Thank you!

  4. Do you know gist.github.com? It’s better for code than WordPress and you can embedd your code in site. You can delete this comment after reading.

  5. It’s working so far. But could you add an example in your blog how you use it in shell script?

  6. Many thanks! A very useful tool and a missing feature in Solaris. I’ve startet to translate this post in german. I will post it in my blog soon – of course with a permalint to yours. As your license allows me to, I will publish your original code in gist.github.com, if you don’t want to do it. You agree?

  7. […] ist die deutsche Übersetzung von Peters Blog Rants about the Solaris OS – Solaris File Event Notification Angenommen, Sie benötigen ein Shell-Script, das irgendeinen Befehl ausführt, sobald eine neue […]

  8. As I understand the code, watchdir monitors directory changes and modified attributes only. So it’s not possible to return the file name which was triggering the event?

    • petersblogwp says:

      That is correct. Actually I don’t think the underlying Solaris File Events Notification (FEN) mechanism has a way of giving that information so it is not because of the watchdir implementation as such.

      If you look at my script example I assume (in that example) that the directory is always empty during wait time. When watchdir completes, i.e. it returns because something has happened on that directory, I then loop over the contents in the directory. Here I simply ASSUME that watchdir was woken up because somebody put a file (or several files) into that directory. There can potentially be other reasons why watchdir was woken up and I should probably test for those using watchdir’s exit value. If watchdir‘s exit value is 0 (zero) then there can be no other reason than something has happened inside the directory. In the example I’m only interested in regular files so I filter everything else away and do not react upon it. Suppose somebody creates a directory inside the directory that watchdir is watching. This will also wake up watchdir and also with an exit value of zero. But I’m not interested in that so I filter it out.

      Given that the Solaris File Events Notification (FEN) mechanism will not tell you exactly WHAT has happened when watching a directory you’ll have to apply logic along the same lines as explained for my script example.

  9. garychow says:

    i ran gcc watchdir.c -o watchdir

    and i am getting errors , the following

    gcc watchdir.c -o watchdir
    gcc: watchdir.c: No such file or directory
    gcc: no input files

  10. garychow says:

    what is the difference between FENS and watchdir

    • petersblogwp says:

      Never heard about the “FENS” abbreviation. Sometimes the ability for Solaris 11 to detect file system changes and ‘report’ them is called FEN (File Event Notification). So FEN would be the name of the ability or perhaps we can say it’s the name of the API. FEN in itself is not operable. It’s just an API. Unfortunately that’s where Sun/Oracle stopped. Not very useful, unless you know C and can build something on top of it, that can then be used from a script or the like.

      This is what watchdir is. watchdir is a specific implementation that uses FEN.

  11. garychow says:

    thanks but i got it to work but not completely, its failing on # Do something with ‘newfile’
    just a simple echo is not working , probably a silly mistake on my part

    • petersblogwp says:

      The shell script is meant as an example stub that uses watchdir. In the shell script in line 44-45 there are some dots to mark that you should put your own stuff there. Perhaps that’s the problem you’re encountering?. Just remove those lines ..but then of course the script will not really do anything.

  12. garychow says:

    Thanks peter for thee quick response, even after removing the dots a simple echo wont work

    • petersblogwp says:

      Then I’m guessing you have some kind of cut’n paste error. WordPress is a lousy platform for code snippets. When you copy something off this page and paste it into something else you’ll for example see quotes becoming ampersand quote; and what not.

      • petersblogwp says:

        Yep. After a short test I’m pretty sure that’s your problem. Look for places where double quotes in the original text have been replaced with nonsense chars. I’ll consider publishing the code somewhere else so that it is easier to grab.

  13. garychow says:

    great thanks

  14. petersblogwp says:

    The source code on this page is now no longer inside WordPress itself (which was the source of cut’n paste problems that people experienced). Instead the source code is now hosted on GitHub.

  15. The reason Solaris File Event Notification doesn’t include the file name is that this would either require the kernel to buffer all events for all consumers (no matter how slow), or provide a different event that meant: ‘Too much data, check it yourself’. The first case makes a DOS attack trivial, the second produces code in applications which is rarely tested and thus will likely not work.

    – Bart

  16. ntamblyn says:

    I cant get this to compile

    watchdir.c: In function ‘process_event’:
    watchdir.c:97:35: error: ‘amp’ undeclared (first use in this function)
    watchdir.c:97:35: note: each undeclared identifier is reported only once for each function it appears in
    watchdir.c:106:29: error: expected ‘)’ before ‘;’ token
    watchdir.c: In function ‘main’:
    watchdir.c:200:40: error: ‘amp’ undeclared (first use in this function)
    watchdir.c:200:43: error: expected ‘)’ before ‘;’ token
    watchdir.c:209:32: error: expected ‘)’ before ‘;’ token
    watchdir.c:248:33: error: expected ‘)’ before ‘;’ token
    watchdir.c:248:33: error: too few arguments to function ‘port_get’
    /usr/include/port.h:24:5: note: declared here

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: