The resmgr_attach() function and its parameters

As you saw in the /dev/null example above, the first thing you'll want to do is register your chosen “mountpoint” with the process manager. This is done via resmgr_attach().

This function has the following prototype:

resmgr_attach (void *dpp,
               resmgr_attr_t *resmgr_attr,
               const char *path,
               enum _file_type file_type,
               unsigned flags,
               const resmgr_connect_funcs_t *connect_funcs,
               const resmgr_io_funcs_t *io_funcs,
               RESMGR_HANDLE_T *handle);

Let's examine these arguments, in order, and see what they're used for.

The dispatch handle. This lets the dispatch interface manage the message receive for your resource manager.
Controls the resource manager characteristics, as discussed above.
The mountpoint that you're registering. If you're registering a discrete mountpoint (such as would be the case, for example, with /dev/null, or /dev/ser1), then this mountpoint must be matched exactly by the client, with no further pathname components past the mountpoint. If you're registering a directory mountpoint (such as would be the case, for example, with a network filesystem mounted as /nfs), then the match must be exact as well, with the added feature that pathnames past the mountpoint are allowed; they get passed to the connect functions stripped of the mountpoint (for example, the pathname /nfs/etc/passwd would match the network filesystem resource manager, and it would get etc/passwd as the rest of the pathname).
The class of resource manager. See below.
Additional flags to control the behavior of your resource manager. These flags are defined below.
connect_funcs and io_funcs
These are simply the list of connect functions and I/O functions that you wish to bind to the mountpoint.
This is an “extendable” data structure (aka “attributes structure”) that identifies the resource being mounted. For example, for a serial port, you'd extend the standard POSIX-layer attributes structure by adding information about the base address of the serial port, the baud rate, etc. Note that it does not have to be an attributes structure—if you're providing your own “open” handler, then you can choose to interpret this field any way you wish. It's only if you're using the default iofunc_open_default() handler as your “open” handler that this field must be an attributes structure.

The flags member can contain any of the following flags (or the constant 0 if none are specified):

These flags indicate that your resource manager wishes to be placed before or after (respectively) other resource managers with the same mountpoint. These two flags would be useful with unioned (overlaid) filesystems. We'll discuss the interactions of these flags shortly.
This flag indicates that your resource manager is taking over the specified mountpoint and below—it's effectively a filesystem style of resource manager, as opposed to a discretely-manifested resource manager.
If set, prevents resolving to any other manager below your mount point except for the path manager. This effectively eliminates unioning on a path.
This ensures that only requests that have the same _FTYPE_* as the file_type passed to resmgr_attach() are matched.
This flag is used when a resource manager wants to catch all client requests, even those with a different _FTYPE_* specification than the one passed to resmgr_attach() in the file_type argument. This can only be used in conjunction with a registration file type of _FTYPE_ALL.
Allow this resource manager to talk to itself. This really is a “Don't try this at home, kids” kind of flag, because allowing a resource manager to talk to itself can break the send-hierarchy and lead to deadlock (as was discussed in the Message Passing chapter).

You can call resmgr_attach() as many times as you wish to mount different mountpoints. You can also call resmgr_attach() from within the connect or I/O functions—this is kind of a neat feature that allows you to “create” devices on the fly.

Note: Your resource manager needs certain abilities enabled when it calls resmgr_attach():

For more information, see the entry procmgr_ability() in the QNX Neutrino C Library Reference.

When you've decided on the mountpoint, and want to create it, you'll need to tell the process manager if this resource manager can handle requests from just anyone, or if it's limited to handling requests only from clients who identify their connect messages with special tags. For example, consider the POSIX message queue (mqueue) driver. It's not going to allow (and certainly wouldn't know what to do with) “regular” open() messages from any old client. It will allow messages only from clients that use the POSIX mq_open(), mq_receive(), and so on, function calls. To prevent the process manager from even allowing regular requests to arrive at the mqueue resource manager, mqueue specified _FTYPE_MQUEUE as the file_type parameter. This means that when a client requests a name resolution from the process manager, the process manager won't even bother considering the resource manager during the search unless the client has specified that it wants to talk to a resource manager that has identified itself as _FTYPE_MQUEUE.

Unless you're doing something very special, you'll use a file_type of _FTYPE_ANY, which means that your resource manager is prepared to handle requests from anyone. For the full list of _FTYPE_* manifest constants, take a look in <sys/ftype.h>.

With respect to the “before” and “after” flags, things get a little bit more interesting. You can specify only one of these flags or the constant 0.

Let's see how this works. A number of resource managers have started, in the order given in the table. We also see the flags they passed for the flags member. Observe the positions they're given:

Resmgr Flag Order
3 0 1, 3, 2
4 _RESMGR_FLAG_BEFORE 1, 4, 3, 2
5 _RESMGR_FLAG_AFTER 1, 4, 3, 5, 2
6 0 1, 4, 6, 3, 5, 2

As you can see, the first resource manager to actually specify a flag always ends up in that position. (From the table, resource manager number 1 was the first to specify the “before” flag; no matter who registers, resource manager 1 is always first in the list. Likewise, resource manager 2 was the first to specify the “after” flag; again, no matter who else registers, it's always last.) If no flag is specified, it effectively acts as a “middle” flag. When resource manager 3 started with a flag of zero, it got put into the middle. As with the “before” and “after” flags, there's a preferential ordering given to all the “middle” resource managers, whereby newer ones are placed in front of other, existing “middle” ones.

However, in reality, there are very few cases where you'd actually mount more than one, and even fewer cases where you'd mount more than two resource managers at the same mountpoint. Here's a design tip: expose the ability to set the flags at the command line of the resource manager so that the end-user of your resource manager is able to specify, for example, -b to use the “before” flag, and -a to use the “after” flag, with no command-line option specified to indicate that a zero should be passed as the flag.

Keep in mind that this discussion applies only to resource managers mounted with the same mountpoint. Mounting /nfs with a “before” flag and /disk2 with an “after” flag will have no effect on each other; only if you were to then mount another /nfs or /disk2 would these flags (and rules) come into play.

Finally, the resmgr_attach() function returns a small integer handle on success (or -1 for failure). This handle can then be used subsequently to detach the pathname from the process manager's internal pathname tables.