Putting in your own functions

When designing your very first resource manager, you'll most likely want to take an incremental design approach. It can be very frustrating to write thousands of lines of code only to run into a fundamental misunderstanding and then having to make the ugly decision of whether to try to kludge (er, I mean “fix”) all that code, or scrap it and start from scratch.

The recommended approach for getting things running is to use the iofunc_func_init() POSIX-layer default initializer function to fill the connect and I/O tables with the POSIX-layer default functions. This means that you can literally write your initial cut of your resource manager as we did above, in a few function calls.

Which function you'll want to implement first really depends on what kind of resource manager you're writing. If it's a filesystem type of resource manager where you're taking over a mountpoint and everything below it, you'll most likely be best off starting with the open connect function handler. (The _RESMGR_FLAG_DIR flag indicates that the pathname should be treated as a directory.) On the other hand, if it's a discretely manifested resource manager that does “traditional” I/O operations (i.e., you primarily access it with client calls like read() and write()), then the best place to start would be the read I/O function handler and/or write I/O function handler. The third possibility is that it's a discretely manifested resource manager that doesn't do traditional I/O operations, but instead relies on devctl() or ioctl() client calls to perform the majority of its functionality. In that case, you'd start at the device control I/O function handler.

Regardless of where you start, you'll want to make sure that your functions are getting called in the expected manner. The really cool thing about the POSIX-layer default functions is that they can be placed directly into the connect or I/O functions table. This means that if you simply want to gain control, perform a printf() to say “I'm here in the io_open!”, and then “do whatever should be done,” you're going to have an easy time of it. Here's a portion of a resource manager that takes over the open connect function handler:

// forward reference
int io_open (resmgr_context_t *, io_open_t *,
             RESMGR_HANDLE_T *, void *);

int
main ()
{
    // everything as before, in the /dev/null example
    // except after this line:
    iofunc_func_init (_RESMGR_CONNECT_NFUNCS, &cfuncs,
                      _RESMGR_IO_NFUNCS, &ifuncs);

    // add the following to gain control:
    cfuncs.open = io_open;

Assuming that you've prototyped the open connect function handler correctly, as in the code example, you can just use the default one from within your own!

int
io_open (resmgr_context_t *ctp, io_open_t *msg,
         RESMGR_HANDLE_T *handle, void *extra)
{
    printf ("I'm here in the io_open!\n");
    return (iofunc_open_default (ctp, msg, handle, extra));
}

In this manner, you're still using the default POSIX-layer iofunc_open_default() handler, but you've also gained control to do a printf().

Obviously, you could do this for the read I/O function handler, write I/O function handler, and device control I/O function handler as well as any others that have POSIX-layer default functions. In fact, this is a really good idea, because it shows you that the client really is calling your resource manager as expected.