Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can you generate a POLLPRI event on a regular file?

Tags:

python

sockets

When working with sysfs GPIO on Linux, you are instructed to poll for POLLPRI and POLLERR events.

This is quite easy:

poll = select.poll()
poll.register(filename, select.POLLPRI | select.POLLERR)
result = poll.poll(timeout=timeout)

However, I would like to write tests for this code, and simulation tests for the application relying on it. So, I need to be able to cause a POLLPRI event.

I have tried using a Unix domain socket, but then I am unable to open the file for reading after the domain socket has connected (errno 6, no such device). I also tried using a socket with SOCK_DGRAM, but that either fails to find the file if not already created, or yields connection refused.

I want a way to open a regular file or create a file that can be opened like a regular file and be able to send it a stream of messages that are treated as "urgent data". ie MSG_OOB.

What can I do?

like image 690
ToBeReplaced Avatar asked Dec 28 '14 04:12

ToBeReplaced


1 Answers

It looks like you can achieve this by polling a sysctl exposed in procfs. If you look at the poll implementation in procfs for the sys subdirectory, you'll see that any sysctl that implements notifications for poll will return a mask that includes POLLERR|POLLPRI. So how do we figure out what sysctls implement this? We look for uses of proc_sys_poll_notify!

One such place is in proc_do_uts_string, which implements a number of sysctls under /proc/sys/kernel. Most of these are read-only, but hostname and domainname can be written (see also their table entries).

Of course, this is going to require root privileges to be able to write to e.g. /proc/sys/kernel/hostname.

This is probably the easiest way to do such a thing while staying within a synthetic filesystem implementation. Of course, the only real way to test your code is to poll(2) one of your pins, press a button, and see if you get your rising / falling signal interrupts.

Note: sysfs also does this for edge nodes in the tree:

>>> import select
>>> f = open('/sys/bus/clockevents/devices/clockevent0/uevent', 'r')
>>> p = select.poll()
>>> p.register(f, select.POLLPRI | select.POLLERR)
>>> result = p.poll(10)
>>> result
[(3, 10)]

10 is of course POLLPRI (0x2) | POLLERR (0x8). I got the same results using /sys/power/state as my input. Basically, if you poll any user-readable, non-directory file entry in sysfs, you'll get POLLPRI | POLLERR back.

like image 76
dho Avatar answered Sep 28 '22 11:09

dho