C Looking for cuse(3) examples

What's the goal? I think cuse(3) is a library, so you have to apply it in C...
I used cuse4bsd as part of a webcam viewer but I don't remember the exact function. Is that what you're talking about? (It's now obsolete and not a port anymore but the source is still online)
 
The goal is use cuse(3) to create a userspace device through which another process can read from or write to said device.

About cuse4bsd - this was written by Hans Selasky. It became a part of freebsd base prior to December 2019. That's why the port was dropped.

So far I can create a device node, and do simple things like 'cat' the device or copy a file to the device. This triggers the expected open/read/close and open/write/close methods. I created a bit of a hack to get the process to gracefully exit. The main event loop blocks - with no timeout. It won't return until it sees - and processes - an event. I've not yet touched the ioctl and poll methods.

I am guessing there are other programs built on this library which might save me from re-inventing the wheel.
 
Are you targeting FreeBSD 13, 14, or 15? The libcuse API has changed slightly over time, so examples can differ depending on the version.

You might want to check /usr/share/examples/cuse if it’s present on your system, or look in the src tree under lib/libcuse/ and sys/dev/cuse/. Those are usually smaller and easier to digest than something like virtual_oss(8).

The blocking main loop behavior is expected — cuse_wait_and_process() is designed to sleep until an event arrives. For graceful shutdown, common approaches include installing a signal handler (e.g., SIGINT/SIGTERM) that sets a termination flag and triggers a wakeup, integrating with poll/select, or generating a dummy event to unblock the loop cleanly.

If your goal is simple IPC via a userspace character device, you may not need ioctl() initially. In many cases, read/write callbacks are sufficient depending on the semantics you need. virtual_oss(8) is large, but the core pattern is fairly small: define a cuse_methods structure, register the device, and run the event loop.

You could also grep the src tree for other libcuse users to see minimal patterns in practice.
 
You might want to check /usr/share/examples/cuse if it’s present on your system, or look in the src tree under lib/libcuse/ and sys/dev/cuse/. Those are usually smaller and easier to digest than something like virtual_oss(8).
I see no example under /usr/share/examples. The other two are the cuse driver and library. I've been digging through them to understand low level concerns.
The blocking main loop behavior is expected — cuse_wait_and_process() is designed to sleep until an event arrives. For graceful shutdown, common approaches include installing a signal handler (e.g., SIGINT/SIGTERM) that sets a termination flag and triggers a wakeup, integrating with poll/select, or generating a dummy event to unblock the loop cleanly.
I'm stuck with issuing an external event for now. FreeBSD does not like it when I try to open the device node from the same process (I think I understand why).

I would have preferred a timeout parameter in cuse_wait_and_process - similar to select(2).
You could also grep the src tree for other libcuse users to see minimal patterns in practice.
I found none. Nothing else calls cuse_wait_and_process.

I did figure out how to use cuse_dev_set_per_file_handle to track per-client state. If, for example, three processes read from the same device, I can track where each one is in the byte stream.

I have so far not needed cuse_alloc_unit_number or cuse_alloc_unit_number_by_id so I've no idea what purpose they serve.
 
You're right — I just checked on my system as well and there are no examples under /usr/share/examples/cuse. That may have existed in older versions, but it’s not present anymore.

I also double-checked the src tree, and there don’t appear to be any other real consumers of libcuse in base besides virtual_oss. So there aren’t any smaller reference implementations to study there.

You're likely correct about the self-open issue. The userspace CUSE server effectively acts as the event dispatcher, so attempting to open the same device from within the same process can easily lead to a deadlock unless you split it across threads or separate processes.

Regarding cuse_wait_and_process(), it’s clearly designed around a purely event-driven model. There’s no timeout parameter, and from the implementation it doesn’t appear intended to behave like select(2) with periodic wakeups. If timeout-style behavior is needed, integrating it into a broader kqueue/poll loop or relying on external signals/events seems to be the intended approach.

As for cuse_alloc_unit_number*(), those are primarily for dynamic minor number allocation when exposing multiple instances (e.g., /dev/foo0, /dev/foo1, etc.). If you're only creating a single static device node, they likely aren't necessary.

At this point your overall design sounds correct — it seems more about structuring the event model cleanly than about missing API pieces.
 
Back
Top