The I/O table is very similar in spirit to the connect functions table just shown above. Here it is, from <sys/resmgr.h>:
typedef struct _resmgr_io_funcs { unsigned nfuncs; int (*read) (ctp, io_read_t *msg, ocb); int (*write) (ctp, io_write_t *msg, ocb); int (*close_ocb) (ctp, void *reserved, ocb); int (*stat) (ctp, io_stat_t *msg, ocb); int (*notify) (ctp, io_notify_t *msg, ocb); int (*devctl) (ctp, io_devctl_t *msg, ocb); int (*unblock) (ctp, io_pulse_t *msg, ocb); int (*pathconf) (ctp, io_pathconf_t *msg, ocb); int (*lseek) (ctp, io_lseek_t *msg, ocb); int (*chmod) (ctp, io_chmod_t *msg, ocb); int (*chown) (ctp, io_chown_t *msg, ocb); int (*utime) (ctp, io_utime_t *msg, ocb); int (*openfd) (ctp, io_openfd_t *msg, ocb); int (*fdinfo) (ctp, io_fdinfo_t *msg, ocb); int (*lock) (ctp, io_lock_t *msg, ocb); int (*space) (ctp, io_space_t *msg, ocb); int (*shutdown) (ctp, io_shutdown_t *msg, ocb); int (*mmap) (ctp, io_mmap_t *msg, ocb); int (*msg) (ctp, io_msg_t *msg, ocb); int (*reserved) (ctp, void *msg, ocb); int (*dup) (ctp, io_dup_t *msg, ocb); int (*close_dup) (ctp, io_close_t *msg, ocb); int (*lock_ocb) (ctp, void *reserved, ocb); int (*unlock_ocb) (ctp, void *reserved, ocb); int (*sync) (ctp, io_sync_t *msg, ocb); int (*power) (ctp, io_power_t *msg, ocb); int (*acl) (ctp, io_acl_t *msg, ocb); int (*pause) (ctp, void *reserved, ocb); int (*unpause) (ctp, io_pulse_t *msg, ocb); int (*read64) (ctp, io_read_t *msg, ocb); int (*write64) (ctp, io_write_t *msg, ocb); int (*notify64) (ctp, io_notify_t *msg, ocb); int (*utime64) (ctp, io_utime_t *msg, ocb); } resmgr_io_funcs_t;
For this structure as well, I've shortened the prototype by removing the type of the ctp member (resmgr_context_t *) and the last member (ocb, of type RESMGR_OCB_T *). For example, the full prototype for read is really:
int (*read) (resmgr_context_t *ctp, io_read_t *msg, RESMGR_OCB_T *ocb);
The very first member of the structure (nfuncs) indicates how big the structure is (how many members it contains). The proper manifest constant for initialization is _RESMGR_IO_NFUNCS.
Note that the parameter list in the I/O table is also very regular. The first parameter is the ctp, and the second parameter is the msg, just as they were in the connect table handlers.
The third parameter is different, however. It's an ocb, which stands for Open Context Block. It holds the context that was bound by the connect message handler (e.g., as a result of the client's open() call), and is available to the I/O functions.
As discussed above, when it comes time to fill in the two tables, we recommend that you use the iofunc_func_init() function to load up the tables with the POSIX-layer default handler routines. Then, if you need to override some of the functionality of particular message handlers, you'd simply assign your own handler function instead of the default routine. We'll see this in the section Putting in your own functions.