Monday Aug 24, 2009

File notification in OpenSolaris - and a bit of shell internals

A reasonably frequently asked question for people porting apps to OpenSolaris is about file notification events.  Linux has inotifyfor doing this.  OpenSolaris has a generic port event system.  The port_create(3C) and port_associate(3C) man pages describe this.  However they don't have what I'd consider the simple example that is likely to be of interest to people familar with the Linux inotify system.   So when the question came up on opensolaris-code@opensolaris.org  again I decided to sketch out the simple pathname based solution.

I hit three interesting problems in implementing what you will see below is a very small amount of code.

1) It wasn't imediatedly obvious to me that you had to call port_associate(3C) again after port_get(3C).

2) If I had read the man page properly I would have noticed the difference between passing NULL for port_associate(3C) timeout versus a zero'd timestruc_t - they effectively mean the opposite thing.

3) I kept geting an event when I ran pstack(1) on my program.  This was very strange, but thanks to truss I worked out what was going on.   I'll leave the answer to after the sample code...

#include <stdio.h>
#include <port.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <poll.h>
#include <unistd.h>
#include <strings.h>
#include <errno.h>

int
main(int argc, char \*\*argv)
{
        int myport = port_create();
        timespec_t timeout;
        file_obj_t mysf = { 0 };
        struct stat sbuf;
        int myfd;
        port_event_t pe;
        int ret;


        myfd = open("/tmp/test", O_RDONLY);

        fstat(myfd, &sbuf);

        for (;;) {
                mysf.fo_name = "/tmp/test";
                port_associate(myport, PORT_SOURCE_FILE, (uintptr_t)&mysf,
                    FILE_MODIFIED, NULL);

                printf("Waiting for events...");

                errno = 0;
                ret = port_get(myport, &pe, NULL);
                if (ret != 0) {
                        switch (errno) {
                        case EINTR:
                            continue;
                        case ETIME:
                            printf("Timer expired\\n");
                            break;
                        default:
                            perror("port_get");
                            return (1);
                        }
                }

                printf("woke \\n");
        }

} 

The issue with my original version was that the directory I used was "/tmp".  This where we learn a little about shell internals.   I didn't just do pstack on my process what I actually did was:

$ pstack `pgrep file_event`

When I stopped and thought about it for a bit it was obvious that the event was a shell caused side effect.  Even then I couldn't work out what it was until I resorted to using truss from another terminal:

$ truss -f -topen -p 25172
...

8493: open("/tmp/.stdout2NaOLq", O_RDWR|O_CREAT|O_EXCL, 0600) = 5 8493: open("/tmp/.stderr3NaOLq", O_RDWR|O_CREAT|O_EXCL, 0600) = 5 ...

and there it is. The shell as take the output of the "backticked" command and put it in a temporary file in /tmp.  Which is why I've changed the sample code above to use a subdir of /tmp.

About

DarrenMoffat

Search

Categories
Archives
« April 2014
MonTueWedThuFriSatSun
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
17
18
19
20
21
22
23
24
25
26
27
28
29
30
    
       
Today