Extending mqueuefs to allow monitoring queue depth with ls(1)

mqueuefs(5) is a kernel module that is normally not loaded. When loaded, the associated POSIX message queue mq_xxxx functions are enabled for applications.

You can go one step further and mount mqueuefs to provide a view into your message queues using tools like cat(1) and ls(1). One thing I noticed about the listed queues - using ls(1) - is the file size is always zero so I dove into the kernel source to see why.

The kernel module is located here.

https://github.com/freebsd/freebsd-src/blob/main/sys/kern/uipc_mqueue.c

On your local system - assuming /usr/src - it would be here:

/usr/src/sys/kern/uipc_mqueue.c

The makefile to build only mqueuefs is here. This is where you'll run make(1).

https://github.com/freebsd/freebsd-src/tree/main/sys/modules/mqueue/Makefile

Likewise, on your local system - assuming /usr/src - it would be here:

/usr/src/sys/modules/mqueue/Makefile

When you build the module, the output appears under /usr/obj. The exact path depends on your architecture. In my case, it appears here:

/usr/obj/usr/src/amd64.amd64/sys/modules/mqueue/

After some digging I found the kernel module reported the queue depth in response to a read ioctl. The function that does so here:


This is useful because it shows us how access the 'mqueue' structure to get the current message depth.

Another interesting area of the code is the mqfs_getattr function here:


Check out this line:


I'll quote it here:

Code:
vap->va_size = 0;

I found I could set va_size to an arbitrary value - and it would show up as the file size in ls(1). In theory, we could make a one line edit to pull the queue depth from the mqueue structure and stuff it into va_size.

Well, almost. Since mqueuefs is emulating a file system directory, it includes entries for "." and "..". Those won't have a mqueue structure associated with them so we need to check the node type. Keen eyes will see va_size is set to zero twice. See lines 1207 and 1209. We need to modify line 1209 to only set va_bytes to zero. Line 1207 will be replaced by several lines. Something like this:

Code:
if (pn->mn_type == mqfstype_file)
{
    struct mqueue *mq = pn->mn_data;
    vap->va_size = mq->mq_curmsgs; /* displayed in ls(1) */
}
else
    vap->va_size = 0;

Don't forget to fix the line that sets va_bytes to look like this:

Code:
vap->va_bytes = 0;

Now you can cd(1) to the folder above with the makefile and run make(1).

If all goes well, you will now have a new mqueuefs.ko file in the output folder mentioned above under /usr/obj.

To test this new module, first make a backup of your existing mqueuefs.ko module found in /boot/kernel. Maybe rename it to mqueuefs.ko.original. Now you can copy your new mqueuefs.ko file to /boot/kernel.

If the mqueuefs kernel module was previously loaded - you may need to reboot. After rebooting and logging in, run the following (pick a mount point appropriate to your system):


kldload mqueuefs
mount -t mqueuefs null /some/mount/point


Now you can list your queues using ls(1). Of course, you'll need to run some code to create the queues and then stuff them with some data to see anything interesting in ls(1). You can run touch(1) to create a queue but will need to resource to some code to fill the queue with data.

POSIX message queues are not durable or persisted so once you shutdown or reboot - any data therein is lost.

I chose to pass 'number of messages' into the file size field instead of 'total number of bytes' because this value is more useful to me but there's nothing stopping you from displaying the number of bytes instead.
 
Back
Top