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.

Monday Aug 10, 2009

Sending a Break to Solaris hosted in Virtualbox

I've recently starting using VirtualBox instead of physical machines for some of my basic functional testing.  When doing some types of kernel development it is often necessary to force the system into kmdb.

The F1-A keystroke does this on Solaris x86 systems by default, however that isn't going to work with VirtualBox because that keystore will be grabbed by some very low level kernel routines in the host an never reaches the guest.

So we need an alternate way of getting a break to the guest Solaris from the host one.

I was sure someone else must have worked this out before.  I didn't get the full answer from a quick google search but I did find all the parts.

The CLI for VirtualBox can send an NMI (Non Maskable Interupt) to any running guest. Solaris can beconfigured to drop into kmdb or force a panic when receiving an NMI.

In the guest put this into /etc/system and reboot:

set pcplusmp:apic_kmdb_on_nmi=1

Or to set it interactively do:

# echo apic_kmdb_on_nmi/W1 | mdb -kw

# mdb -K

Then with the VirtualBox CLI we can send an NMI to our guest:

$ VBoxManage debugvm ZFS_Crypto_Test injectnmi

Nice easy solution.  Though I do now wonder why we don't have some default action for when an NMI is received - but then not everyone cares about getting a dump or getting into kmdb!

Updated 2013-10-8: at somepoint this changed from controlvm to debugvm

About

DarrenMoffat

Search

Categories
Archives
« August 2009 »
MonTueWedThuFriSatSun
     
1
2
3
4
5
6
7
8
9
11
12
13
14
15
16
17
18
19
20
21
22
23
25
26
27
28
29
30
31
      
Today