Linux Kernel Module/IOCTL: inappropriate ioctl for device - c

I am in the process of writing a Linux Kernel Module (LKM) serving as a pseudo-driver - I am unable to figure out how to make IOCTL calls between the LKM (wait.c) and the user-level program (user.c).
The magic number for the device driver is 0xBF - the LKM does not communicate with a physical block/char device, it is simply an exercise. From what I can tell, the IOCTL call to KERN_IOCTL_CREATE_EVENT is not formatted properly & the magic number is incorrect.
The IOCTL call that I am attempting to use is:
#include <sys/ioctl.h>
#define KERN_IOCTL_CREATE_EVENT _IOWR(WAIT_DEVICE_MAGIC, 1, int)
int main(){
int ret;
int fd;
fd = open("/dev/wait", 0);
if(fd < 0){
return -1;
}
ret = ioctl(fd, KERN_IOCTL_CREATE_EVENT, 0);
Error:
[fail]: KERN_IOCTL_CREATE_EVENT: Inappropriate ioctl for device
The user-mode application can open/close a file descriptor pointing to the device: /dev/wait but the case/switch statement isn't accepting the IOCTL call. Any suggestions?
Here is the output of # uname -a
Linux vagrant-ubuntu-trusty-64 3.13.11.11+ #1 SMP Mon Dec 1 20:50:23 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux
wait.c
#include <linux/miscdevice.h>
#include <linux/moduleparam.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <asm/uaccess.h>
#include <linux/sched.h>
#include <linux/ioctl.h>
#include <linux/cdev.h>
#include <linux/init.h>
#include <linux/wait.h>
#include <linux/fs.h>
#include "wait.h"
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Tyler Fisher <tyler#tylerfisher.org>");
MODULE_DESCRIPTION("In-kernel wait queue");
static unsigned long event_table_size = 50;
module_param(event_table_size, ulong, (S_IRUSR | S_IRGRP | S_IROTH));
MODULE_PARM_DESC(event_table_size, "Size of event table (i.e. how many processes can be blocking)");
/* IOCTL function headers */
static int wait_open(struct inode *, struct file *);
static int wait_close(struct inode *, struct file *);
static long wait_ioctl(struct inode *, struct file *, unsigned int, unsigned long);
/* other function headers */
static long event_open(int event_id);
/* file operations */
static struct file_operations wait_fops = {
.owner = THIS_MODULE,
.open = wait_open,
.release = wait_close,
.llseek = noop_llseek,
.unlocked_ioctl = wait_ioctl
};
/* device handler */
static struct miscdevice wait_misc_device = {
.minor = MISC_DYNAMIC_MINOR,
.name = WAIT_DEVICE_NAME,
.fops = &wait_fops
};
/* open wait device */
static int wait_open(struct inode *inode, struct file *file){
dev_t node = iminor(inode);
if(MINOR(node) != WAIT_DEVICE_MINOR){
return -ENODEV;
}
return 0;
}
static int wait_close(struct inode *inode, struct file *file){
return 0;
}
static long wait_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long sub_cmd){
switch(cmd){
case KERN_IOCTL_CREATE_EVENT:
printk(KERN_INFO "[wait device]: create event %lu\n", sub_cmd);
return event_open(sub_cmd);
default:
return -ENOENT;
}
}
static long event_open(int id){
return 0;
}
static long __init wait_init(void){
if(misc_register(&wait_misc_device) < 0){
printk(KERN_ERR "[wait device] failed to register device\n");
return -1;
}
printk(KERN_INFO "[wait device] has been registered\n");
return 0;
}
static void __exit wait_exit(void){
misc_deregister(&wait_misc_device);
printk(KERN_INFO "[wait device] has been unregistered\n");
}
module_init(wait_init);
module_exit(wait_exit);
wait.h
#include <linux/ioctl.h>
#define WAIT_DEVICE_NAME "wait"
#define WAIT_DEVICE_MAGIC 0xBF
#define WAIT_DEVICE_MAJOR 200
#define WAIT_DEVICE_MINOR 0
#define KERN_IOCTL_CREATE_EVENT _IOWR(WAIT_DEVICE_MAGIC, 0x01, int)
#define MAX_WAITING 5
The test program for the IOCTL calls:
user.c
#include <sys/ioctl.h>
#include <fcntl.h>
#include <stdio.h>
#define WAIT_DEVICE_MAGIC 0xBF
#define KERN_IOCTL_CREATE_EVENT _IOWR(WAIT_DEVICE_MAGIC, 0x01, int)
#define KERN_IOCTL_DESTROY_EVENT _IOWR(WAIT_DEVICE_MAGIC, 0x02, int)
#define KERN_IOCTL_LOCK_EVENT _IOWR(WAIT_DEVICE_MAGIC, 0x03, int)
#define KERN_IOCTL_UNLOCK_EVENT _IOWR(WAIT_DEVICE_MAGIC, 0x04, int)
int main(){
int fd;
if(fd = open("/dev/wait", O_RDWR) < 0){
perror("failed to open /dev/wait");
return -1;
}
/* test IOCTL: event creation */
if(ioctl(fd, KERN_IOCTL_CREATE_EVENT, 0) < 0){
perror("[fail]: KERN_IOCTL_CREATE_EVENT");
return -1;
}
return 0;
}
Makefile
obj-m += wait.o
CFLAGS_wait.o += -DDEBUG
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
In order to test out the LKM - clear dmesg, compile & execute user.c w/GCC:
# dmesg -c > /dev/null 2>&1
# make
# rmmod wait.ko
# insmod wait.ko
# gcc user.c -o user && ./user
The amount of debugging errors is embarassing. I feel bad for sharing this - and realize that this may cause the issue to be closed/downvoted into oblivion.
# sh test.sh
[+] cleared dmesg
make -C /lib/modules/3.13.11.11+/build M=/home/vagrant/PROG40000-kernel-synchronization modules
make[1]: Entering directory `/home/vagrant/ubuntu-trusty'
CC [M] /home/vagrant/PROG40000-kernel-synchronization/wait.o
/home/vagrant/PROG40000-kernel-synchronization/wait.c:61:1: warning: initialization from incompatible pointer type [enabled by default]
};
^
/home/vagrant/PROG40000-kernel-synchronization/wait.c:61:1: warning: (near initialization for ‘wait_fops.unlocked_ioctl’) [enabled by default]
In file included from include/linux/moduleparam.h:4:0,
from /home/vagrant/PROG40000-kernel-synchronization/wait.c:11:
/home/vagrant/PROG40000-kernel-synchronization/wait.c: In function ‘__inittest’:
include/linux/init.h:297:4: warning: return from incompatible pointer type [enabled by default]
{ return initfn; } \
^
/home/vagrant/PROG40000-kernel-synchronization/wait.c:167:1: note: in expansion of macro ‘module_init’
module_init(wait_init);
^
Building modules, stage 2.
MODPOST 1 modules
CC /home/vagrant/PROG40000-kernel-synchronization/wait.mod.o
LD [M] /home/vagrant/PROG40000-kernel-synchronization/wait.ko
make[1]: Leaving directory `/home/vagrant/ubuntu-trusty'
[--dmesg--]
[13112.810008] [wait device] has been unregistered
[13112.819049] [wait device] has been registered
[-/dmesg--]
[+] compiled user-mode program
-----
[fail]: KERN_IOCTL_CREATE_EVENT: Inappropriate ioctl for device
[fail]: KERN_IOCTL_CREATE_EVENT: Inappropriate ioctl for device
[+] executed user-mode program
-----
[--dmesg--]
[13112.810008] [wait device] has been unregistered
[13112.819049] [wait device] has been registered
[13112.893049] SOMEONE DARE READ FROM ME!?
[13112.893057] [wait device] invalid magic number: 0:0:191
[13112.893535] [wait device] invalid magic number: 0:0:191
[-/dmesg--]

Okay. So. Here's the solution.
In Linux kernel 2.6.x the declaration for _ioctl calls changed from
static long wait_ioctl(struct inode *, struct file *, unsigned int, unsigned long);
To:
static long wait_ioctl(struct file *, unsigned int, unsigned long);
The fix is thus:
...
static long wait_ioctl(struct file *, unsigned int, unsigned long);
...
static long wait_ioctl(struct file *file, unsigned int cmd, unsigned long sub_cmd){
if(_IOC_TYPE(cmd) != WAIT_DEVICE_MAGIC){
printk(KERN_INFO "[wait device] invalid magic number: %u:%u:%u", _IOC_TYPE(cmd), cmd, WAIT_DEVICE_MAGIC);
return -ENOTTY;
}
....

.compat_ioctl
Also make sure to implement this file_operation if you are making 32-bit calls to a 64-bit kernel.
The symptom is that your ioctl handler is never run.

Related

errors with C code (reads GPIO input off a Raspberry Pi Zero W), compiles on Jessie but doesn't compile on Buster Lite

I am very new to C (had one semester class like 5 years ago). To summarize my problem, I have code written in C that compiles on Raspbian Jessie, but not Buster Lite.
I'm using a Raspberry Pi Zero W + ADCs to input an analog signal, digitize it, and transmit it wirelessly and the code I'm having issues with takes the data from the GPIOs and stores it in a buffer that I can read from. I previously was running Raspbian Jessie on my pi and I had NO issues compiling the code (no warnings, no errors). It was correctly reading data from the ADCs and storing it in the buffer. When I installed Raspbian Buster Lite on my Pi, and have been getting multiple errors/warnings when compiling. (I'm not concerned about the warnings/notes, know how to fix them, just the errors. partly included because I don't understand why they appeared on Buster but not on Jessie)
make -C /lib/modules/4.19.97+/build M=/home/pi modules
make[1]: Entering directory '/usr/src/linux-headers-4.19.97+'
CC [M] /home/pi/adc.o
/home/pi/adc.c:132:9: warning: useless storage class specifier in empty declaration
};
^
/home/pi/adc.c: In function ‘readScope’:
/home/pi/adc.c:176:13: warning: ISO C90 forbids mixed declarations and code [-Wdeclaration-after-statement]
struct timespec ts_start,ts_stop;
^~~~~~
/home/pi/adc.c:218:29: error: assignment to ‘unsigned char *’ from incompatible pointer type ‘struct DataStruct *’ [-Werror=incompatible-pointer-types]
ScopeBufferStart=&dataStruct;
^
/home/pi/adc.c: At top level:
/home/pi/adc.c:225:9: warning: "/*" within comment [-Wcomment]
/*
In file included from ./include/linux/printk.h:7,
from ./include/linux/kernel.h:14,
from /home/pi/adc.c:1:
/home/pi/adc.c: In function ‘init_module’:
./include/linux/kern_levels.h:5:18: warning: too many arguments for format [-Wformat-extra-args]
#define KERN_SOH "\001" /* ASCII Start Of Header */
^~~~~~
./include/linux/kern_levels.h:9:20: note: in expansion of macro ‘KERN_SOH’
#define KERN_ALERT KERN_SOH "1" /* action must be taken immediately */
^~~~~~~~
/home/pi/adc.c:248:24: note: in expansion of macro ‘KERN_ALERT’
printk(KERN_ALERT,"Failed to map the physical GPIO registers into the virtual memory space.\n");
^~~~~~~~~~
/home/pi/adc.c:283:13: warning: ISO C90 forbids mixed declarations and code [-Wdeclaration-after-statement]
struct bcm2835_peripheral *p=&myclock;
^~~~~~
/home/pi/adc.c:290:13: warning: ISO C90 forbids mixed declarations and code [-Wdeclaration-after-statement]
int speed_id = 6; //1 for to start with 19Mhz or 6 to start with 500 MHz
^~~
/home/pi/adc.c: In function ‘device_read’:
/home/pi/adc.c:359:35: warning: comparison of distinct pointer types lacks a cast
while (length && buf_p<ScopeBufferStop) {
^
In file included from /home/pi/adc.c:4:
./arch/arm/include/asm/uaccess.h:387:33: error: incompatible types when initializing type ‘char’ using type ‘struct DataStruct’
__typeof__(*(ptr)) __pu_val = (x); \
^
./arch/arm/include/asm/uaccess.h:404:2: note: in expansion of macro ‘__put_user_switch’
__put_user_switch((x), (ptr), __pu_err, __put_user_check); \
^~~~~~~~~~~~~~~~~
/home/pi/adc.c:361:23: note: in expansion of macro ‘put_user’
if(0!=put_user(*(buf_p++), buffer++))
^~~~~~~~
At top level:
/home/pi/adc.c:111:26: warning: ‘ScopeBuffer_Ptr’ defined but not used [-Wunused-variable]
static uint32_t *ScopeBuffer_Ptr;
^~~~~~~~~~~~~~~
cc1: some warnings being treated as errors
make[2]: *** [scripts/Makefile.build:310: /home/pi/adc.o] Error 1
make[1]: *** [Makefile:1522: _module_/home/pi] Error 2
make[1]: Leaving directory '/usr/src/linux-headers-4.19.97+'
make: *** [Makefile:4: all] Error 2
What I've tried:
One of the first errors I got (code updated since then, used to have buf_ declared as const char *buf_p;) is that I'm using incompatible pointer types:
/home/pi/adc.c:262:19: error: assignment to ‘unsigned char *’ from incompatible pointer type ‘struct DataStruct *’ [-Werror=incompatible-pointer-types]
ScopeBufferStart=&dataStruct;
When I fix this (by redefining the variable buf_p as a struct DataStruct), I get a new error in the device_read function that I haven't been able to fix:
In file included from /home/pi/adc.c:13:
./arch/arm/include/asm/uaccess.h:387:33: error: incompatible types when initializing type ‘char’ using type ‘struct DataStruct’
__typeof__(*(ptr)) __pu_val = (x); \
I also noticed that the output from the Makefile on Buster Lite I get
make -C /lib/modules/4.19.97+/build M=/home/pi modules
make[1]: Entering directory '/usr/src/linux-headers-4.19.97+'
...
...
make[1]: Leaving directory '/usr/src/linux-headers-4.19.97+'
while on Jessie I get
make -C /lib/modules/`uname -r`/build M=$PWD
make[1]: Entering directory '/usr/src/linux-source-4.4.50+'
Building modules, stage 2.
MODPOST 1 modules
make[1]: Leaving directory '/usr/src/linux-source-4.4.50+'
Here is my code...
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
#include <linux/time.h>
#include <linux/io.h>
#include <linux/vmalloc.h>
#include <linux/delay.h>
#include <linux/fcntl.h> /*Helps fix O_ACCMODE*/
#include <linux/sched.h> /*Helps fix TASK_UNINTERRUPTIBLE */
#include <linux/fs.h> /*Helps fix the struct intializer */
int __init init_module(void);
void __exit cleanup_module(void);
static int device_open(struct inode *, struct file *);
static int device_release(struct inode *, struct file *);
static ssize_t device_read(struct file *, char *, size_t, loff_t *);
static ssize_t device_write(struct file *, const char *, size_t, loff_t *);
#define SUCCESS 0
#define DEVICE_NAME "stdin"// Dev name
#define BUF_LEN 80//Max length of device message
//---------------------------------------------------------------------------------------------------------
//Things for the GPIO Port
#define BCM2708_PERI_BASE 0x20000000
//if you're using a RPi3, PERIBASE value should be changed to 0x3F000000
#define GPIO_BASE (BCM2708_PERI_BASE + 0x200000) // GPIO controller
// depends on the RPi
#define INP_GPIO(g) *(gpio.addr + ((g)/10)) &= ~(7<<(((g)%10)*3))
#define OUT_GPIO(g) *(gpio.addr + ((g)/10)) |= (1<<(((g)%10)*3)) //001
//alternative function
#define SET_GPIO_ALT(g,a) *(gpio.addr + (((g)/10))) |= (((a)<=3?(a) + 4:(a)==4?3:2)<<(((g)%10)*3))
#define GPIO_SET *(gpio.addr + 7) // sets bits which are 1 ignores bits which are 0
#define GPIO_CLR *(gpio.addr + 10) // clears bits which are 1 ignores bits which are 0
#define GPIO_READ(g) *(gpio.addr + 13) &= (1<<(g))
//GPIO Clock
#define CLOCK_BASE (BCM2708_PERI_BASE + 0x00101000)
#define GZ_CLK_BUSY (1 << 7)
//---------------------------------------------------------------------------------------------------------
//How many samples to capture
#define SAMPLE_SIZE 10000 // 2x2500 pts in one line
#define REPEAT_SIZE 10 // 10 captures
//Define GPIO Pins
//ADC 1
#define BIT0_ADC1 16
#define BIT1_ADC1 17
#define BIT2_ADC1 18
#define BIT3_ADC1 19
#define BIT4_ADC1 20
#define BIT5_ADC1 22
#define BIT6_ADC1 25
#define BIT7_ADC1 26
#define BIT8_ADC1 27
//ADC 2
#define BIT0_ADC2 7
#define BIT1_ADC2 8
#define BIT2_ADC2 9
#define BIT3_ADC2 10
#define BIT4_ADC2 11
#define BIT5_ADC2 12
#define BIT6_ADC2 13
#define BIT7_ADC2 14
#define BIT8_ADC2 15
// Pulser
#define Puls_ON 23
#define Puls_OFF 24
#define PPWWMM 6
#define MY_NOP(__N) __asm ("nop"); // or sth like "MOV R0,R0"
//---------------------------------------------------------------------------------------------------------
// IO Acces
struct bcm2835_peripheral {
unsigned long addr_p;
int mem_fd;
void *map;
volatile unsigned int *addr;
};
static int map_peripheral(struct bcm2835_peripheral *p);
static void unmap_peripheral(struct bcm2835_peripheral *p);
static void readScope(void);
static int Major; /* Major number assigned to our device driver */
static int Device_Open = 0; /* Is device open?
* Used to prevent multiple access to device */
static char msg[BUF_LEN]; /* The msg the device will give when asked */
static char *msg_Ptr;
static uint32_t *ScopeBuffer_Ptr;
// static unsigned char *buf_p;
static struct file_operations fops = {
.read = device_read,
.write = device_write,
.open = device_open,
.release = device_release
};
//--------------------------------------------------------------------------------------------------------
static struct bcm2835_peripheral myclock = {CLOCK_BASE};
static struct bcm2835_peripheral gpio = {GPIO_BASE};
static struct DataStruct{
uint32_t Buffer[REPEAT_SIZE*SAMPLE_SIZE];
uint32_t time;
};
struct DataStruct dataStruct;
struct DataStruct *buf_p;
static unsigned char *ScopeBufferStart;
static unsigned char *ScopeBufferStop;
//---------------------------------------------------------------------------------------------------------
static int map_peripheral(struct bcm2835_peripheral *p)
{
p->addr=(uint32_t *)ioremap(GPIO_BASE, 41*4); //41 GPIO register with 32 bit (4*8)
return 0;
}
static void unmap_peripheral(struct bcm2835_peripheral *p) {
iounmap(p->addr);//unmap the address
}
//---------------------------------------------------------------------------------------------------------
static void readScope(){
int counter=0;
int counterline = 0;
int limit = 0;
int Pon=0;
int Poff=0;
//int Fail=0;
OUT_GPIO(Puls_ON);
OUT_GPIO(Puls_OFF);
GPIO_SET = 1 << Puls_ON;
GPIO_CLR = 1 << Puls_OFF;
msleep(10);
//disable IRQ
local_irq_disable();
local_fiq_disable();
struct timespec ts_start,ts_stop;
//Start time
set_current_state(TASK_UNINTERRUPTIBLE);
getnstimeofday(&ts_start);
while(counterline<REPEAT_SIZE){
Pon = 0;
Poff = 0;
limit = (counterline+1)*SAMPLE_SIZE;
while(counter<(limit) ){
dataStruct.Buffer[counter++]= *(gpio.addr + 13);
}
// to avoid freezes
// msleep(0.5);
counterline++;
}
//Stop time
getnstimeofday(&ts_stop);
INP_GPIO(Puls_ON);
INP_GPIO(Puls_OFF);
set_current_state(TASK_INTERRUPTIBLE);
//enable IRQ
local_fiq_enable();
local_irq_enable();
//save the time difference
dataStruct.time=timespec_to_ns(&ts_stop)-timespec_to_ns(&ts_start);//ns resolution
// buf_p=&dataStruct;//cound maybe removed
buf_p=&dataStruct;//cound maybe removed
ScopeBufferStart=&dataStruct;
ScopeBufferStop=ScopeBufferStart+sizeof(struct DataStruct);
}
//---------------------------------------------------------------------------------------------------------
/*
/*
* This function is called when the module is loaded
*/
int init_module(void)
{
Major = register_chrdev(0, DEVICE_NAME, &fops);
if (Major < 0) {
printk(KERN_ALERT "Registering char device failed with %d\n", Major);
return Major;
}
printk(KERN_INFO "I was assigned major number %d. To talk to\n", Major);
printk(KERN_INFO "the driver, create a dev file with\n");
printk(KERN_INFO "'mknod /dev/%s c %d 0'.\n", DEVICE_NAME, Major);
printk(KERN_INFO "Try various minor numbers. Try to cat and echo to\n");
printk(KERN_INFO "the device file.\n");
printk(KERN_INFO "Remove the device file and module when done.\n");
//Map GPIO
if(map_peripheral(&gpio) == -1)
{
printk(KERN_ALERT,"Failed to map the physical GPIO registers into the virtual memory space.\n");
return -1;
}
//Define Scope pins
// ADC1
INP_GPIO(BIT0_ADC1);
INP_GPIO(BIT1_ADC1);
INP_GPIO(BIT2_ADC1);
INP_GPIO(BIT3_ADC1);
INP_GPIO(BIT4_ADC1);
INP_GPIO(BIT5_ADC1);
INP_GPIO(BIT6_ADC1);
INP_GPIO(BIT7_ADC1);
INP_GPIO(BIT8_ADC1);
// ADC2
INP_GPIO(BIT0_ADC2);
INP_GPIO(BIT1_ADC2);
INP_GPIO(BIT2_ADC2);
INP_GPIO(BIT3_ADC2);
INP_GPIO(BIT4_ADC2);
INP_GPIO(BIT5_ADC2);
INP_GPIO(BIT6_ADC2);
INP_GPIO(BIT7_ADC2);
INP_GPIO(BIT8_ADC2);
// Setting pins for pulser
OUT_GPIO(Puls_ON);
OUT_GPIO(Puls_OFF);
GPIO_CLR = 1 << Puls_ON; // set pulser at 0
GPIO_SET = 1 << Puls_OFF; // set damper at 1
//Set a clock signal on Pin 4
struct bcm2835_peripheral *p=&myclock;
p->addr=(uint32_t *)ioremap(CLOCK_BASE, 41*4);
INP_GPIO(4);
SET_GPIO_ALT(4,0);
// Preparing the clock
int speed_id = 6; //1 for to start with 19Mhz or 6 to start with 500 MHz
*(myclock.addr+28)=0x5A000000 | speed_id; //Turn off the clock
while (*(myclock.addr+28) & GZ_CLK_BUSY) {}; //Wait until clock is no longer busy (BUSY flag)
*(myclock.addr+29)= 0x5A000000 | (0x29 << 12) | 0;//Set divider //divide by 50 (0x32) -- ideally 41 (29) to fall on 12MHz clock
*(myclock.addr+28)=0x5A000010 | speed_id;//Turn clock on
return SUCCESS;
}
//---------------------------------------------------------------------------------------------------------
/*
* This function is called when the module is unloaded
*/
void cleanup_module(void)
{
unregister_chrdev(Major, DEVICE_NAME);
unmap_peripheral(&gpio);
unmap_peripheral(&myclock);
}
//---------------------------------------------------------------------------------------------------------
/*
* Called when a process tries to open the device file, like
* "cat /dev/mycharfile"
*/
static int device_open(struct inode *inode, struct file *file)
{
static int counter = 0;
if (Device_Open)
return -EBUSY;
Device_Open++;
sprintf(msg, "I already told you %d times Hello world!\n", counter++);
msg_Ptr = msg;
readScope();//Read n Samples into memory
try_module_get(THIS_MODULE);
return SUCCESS;
}
//---------------------------------------------------------------------------------------------------------
/*
* Called when a process closes the device file.
*/
static int device_release(struct inode *inode, struct file *file)
{
Device_Open--; /* We're now ready for our next caller */
module_put(THIS_MODULE);
return 0;
}
//---------------------------------------------------------------------------------------------------------
/*
* Called when a process, which already opened the dev file, attempts to
* read from it.
*/
static ssize_t device_read(struct file *filp,
char *buffer,
size_t length,
loff_t * offset)
{
// Number of bytes actually written to the buffer
int bytes_read = 0;
if (*msg_Ptr == 0)
return 0;
//Check that we do not overfill the buffer
while (length && buf_p<ScopeBufferStop) {
if(0!=put_user(*(buf_p++), buffer++))
printk(KERN_INFO "Problem with copy\n");
length--;
bytes_read++;
}
return bytes_read;
}
//---------------------------------------------------------------------------------------------------------
/*
* Called when a process writes to dev file: echo "hi" > /dev/hello
*/
static ssize_t
device_write(struct file *filp, const char *buff, size_t len, loff_t * off)
{
printk(KERN_ALERT "Sorry, this operation isn't supported.\n");
return -EINVAL;
}
Thank you
So I think I have an answer. I realized I need to compile the linux kernel module on Buster before I can compile my adc code. On the Jessie disk image version I was using, I realized the linux kernel module had already been compiled, I think that's why on Jessie it's able to enter the linux source directory to compile while on Buster it enters the linux headers directory. Thank you

Get NFS client IP address in a kernel module

I'm working on a kernel module to track the operations performed by NFS clients on my server.
I can intercept the file operations using a hacky way (hijacking the vfs layer) but I can't retrieve the IP address of the client.
Is there any information that might be stored in the current task that I can use to obtain the IP address of the NFS client performing an operation?
I know from digging into the source code that nfsd stores a struct nfsd_net in the struct super_block's s_fs_info field, but I can only retrieve it as a struct net pointer. And in nfsd's implementation net_generic method is being used to get the struct nfsd_net pointer (using nfsd_net_id which is the pernet_operations's id).
Can I obtain this id somehow? and if yes, can I use the struct nfsd_net in my kernel module? Is it defined somewhere other than the fs/nfsd/netns.h?
Edit
I'm using this approach to hijack the open function. I'm writing this for kernel version 4.15.0. Here's the code of the kernel module:
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/kobject.h>
#include <linux/string.h>
#include <linux/slab.h>
#include <linux/version.h>
#include <linux/proc_fs.h>
#include <linux/cred.h>
#include <linux/sched.h>
#include <linux/preempt.h>
#include <linux/uaccess.h>
#include <linux/xattr.h>
MODULE_LICENSE("GPL");
#if defined(__i386__)
#define POFF 1
#define CSIZE 6
// push address, addr, ret
char *jmp_code="\x68\x00\x00\x00\x00\xc3";
typedef unsigned int PSIZE;
#else
#define POFF 2
#define CSIZE 12
// mov address to register rax, jmp rax. for normal x64 convention
char *jmp_code="\x48\xb8\x00\x00\x00\x00\x00\x00\x00\x00\xff\xe0";
typedef unsigned long PSIZE;
#endif
DEFINE_SPINLOCK(root_open_lock);
int (*orig_root_open) (struct inode *, struct file *);
void *orig_root_open_code;
void hook(void *src_func,void *dst_addr){
barrier();
write_cr0(read_cr0() & (~0x10000));
memcpy(src_func,jmp_code,CSIZE);
*(PSIZE *)&(((unsigned char*)src_func)[POFF])=(PSIZE)dst_addr;
write_cr0(read_cr0() | 0x10000);
barrier();
}
void save_and_hook(void **p_reserve,void *src_func,void *dst_addr){
barrier();
write_cr0(read_cr0() & (~0x10000));
*p_reserve=kmalloc(CSIZE,GFP_KERNEL);
// save origin code
memcpy(*p_reserve,src_func,CSIZE);
hook(src_func,dst_addr);
write_cr0(read_cr0() | 0x10000);
barrier();
}
void fix(void **p_reserve,void *src_func){
barrier();
write_cr0(read_cr0() & (~0x10000));
memcpy(src_func,*p_reserve,CSIZE);
write_cr0(read_cr0() | 0x10000);
barrier();
}
int fake_root_open(struct inode *x, struct file *fp)
{
int ret;
printk("vfshijack: hijacked open\n"); // I need to find the client ip here.
barrier();
spin_lock(&root_open_lock);
fix(&orig_root_open_code, orig_root_open);
ret = orig_root_open(x, fp);
hook(orig_root_open, fake_root_open);
spin_unlock(&root_open_lock);
barrier();
return ret;
}
int vfs_init(void)
{
struct file *fp = filp_open("/", O_DIRECTORY|O_RDONLY, 0);
if (IS_ERR(fp))
return -1;
orig_root_open = fp->f_op->open;
if(orig_root_open)
{
save_and_hook(&orig_root_open_code, orig_root_open, fake_root_open);
}
filp_close(fp, NULL);
printk("vfshijack: vfshijack loaded\n");
return 0;
}
void vfs_exit(void)
{
if(orig_root_open)
{
fix(&orig_root_open_code, orig_root_open);
}
printk("vfshijack: vfshijack unloaded\n");
}
module_init(vfs_init);
module_exit(vfs_exit);
You can try to get needed information from linux kernel tracing tools without hooking kernel binary with some custom assembly. There are perf, ftrace, trace-cmd for most kernel versions, and stap and lttng for more custom versions. Some documentation to start: https://www.kernel.org/doc/html/v4.18/trace/index.html "Linux Tracing Technologies"
There are several tracepoints defined in nfsd:
# modprobe nfsd
# modprobe nfs
# perf list tracepoint|grep nfs
# find /sys/kernel/debug/tracing/events -type d|grep nfsd
# trace-cmd list -e nfsd:read_start -F
nfsd/read_start and nfsd/write_start tracepoints are good places to start. Both should have access to request structure rqstp with address rq_addr and fh pointer (but some eBPF or stap scripting may be useful)
https://elixir.bootlin.com/linux/v4.15/source/fs/nfsd/vfs.c#L1020
__be32 nfsd_read(struct svc_rqst *rqstp, struct svc_fh *fhp,...)
trace_read_start(rqstp, fhp, offset, vlen);
trace_read_opened(rqstp, fhp, offset, vlen);
trace_read_io_done(rqstp, fhp, offset, vlen);
trace_read_done(rqstp, fhp, offset, vlen);
I have no complete example of trace-cmd or stap usage for nfs daemon tracing.
Systemtap (stap) has some examples of nfsd statistics:
https://github.com/jav/systemtap/blob/master/testsuite/systemtap.examples/index.txt
# stap nfsd_unlink.stp -c "sleep 0.2"
The nfsdtop.stp script gathers and displays NFS lookups
https://github.com/larytet/SystemTap/blob/master/testsuite/systemtap.examples/network/nfsdtop.stp

Kernel: using sigqueue-functions [duplicate]

This question already has answers here:
Can't access super_blocks list from kernel module: undefined!
(2 answers)
Closed 6 years ago.
I try to implement a signal call from a loadable kernel module to a user application. The application uses sigaction to handle the signal call and later it shall use sival_int to handle different actions by switchand case.
void signalHandler_function (int signum,
siginfo_t *siginfo,
void *ucontext)
{
printf("signum: %i\n", signum);
printf("sigval: %d\n", siginfo->si_value.sival_int);
}
int main(){
int ret;
int pid = getpid();
char pidc[4];
struct sigaction sig;
memcpy(pidc,&pid,4);
// configure signal handler
sig.sa_flags = SA_SIGINFO;
sig.sa_sigaction = signalHandler_function;
sigaction(SIGIO, &sig, NULL);
g_fdCharDev = open(EXPDEV_DEVPATHNAME, O_RDWR);
printf("My process ID : %d\n", pid);
ret = write(g_fdCharDev, &pid, 1);
...
}
On kernel side I try to use send_sigqueue() (together with sigqueue_alloc()and sigqueue_free()). This is declared in include/linux/sched.h, line 2320 as extern and defined in kernel/signal.c, line 1560. But the linker says this functions are undefined:
make -C /lib/modules/3.19.0-58-generic/build/ M=/home/alex/git/Kernel3/SignalHandling modules
make[1]: Verzeichnis »/usr/src/linux-headers-3.19.0-58-generic« wird betreten
CC [M] /home/alex/git/Kernel3/SignalHandling/ExpDev.o
Building modules, stage 2.
MODPOST 1 modules
WARNING: "sigqueue_free" [/home/alex/git/Kernel3/SignalHandling/ExpDev.ko] undefined!
WARNING: "sigqueue_alloc" [/home/alex/git/Kernel3/SignalHandling/ExpDev.ko] undefined!
WARNING: "send_sigqueue" [/home/alex/git/Kernel3/SignalHandling/ExpDev.ko] undefined!
CC /home/alex/git/Kernel3/SignalHandling/ExpDev.mod.o
LD [M] /home/alex/git/Kernel3/SignalHandling/ExpDev.ko
make[1]: Verzeichnis »/usr/src/linux-headers-3.19.0-58-generic« wird verlassen
Process terminated with status 0 (0 minute(s), 0 second(s))
0 error(s), 0 warning(s) (0 minute(s), 0 second(s))
Here comes the shortened source of the LKM:
#include <linux/init.h> /// Macros used to mark up functions e.g. __init __exit
#include <linux/module.h> /// Core header for loading LKMs into the kernel
#include <linux/device.h> /// Header to support the kernel Driver Model
#include <linux/kernel.h> /// Contains types, macros, functions for the kernel
#include <linux/fs.h> /// Header for the Linux file system support
#include <asm/uaccess.h> /// Required for the copy to user function
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/rcupdate.h>
#include <linux/slab.h>
struct siginfo *g_sig_info;
static struct sigqueue *g_sigqueue;
int g_usp_pid;
struct task_struct *g_sig_task;
static int __init expdev_init(void)
{
...
// setup signal
g_sigqueue = sigqueue_alloc();
g_sigqueue->info.si_signo = SIGIO;
g_sigqueue->info.si_signo = SI_QUEUE;
g_sigqueue->info.si_errno = 0;
...
}
static void __exit expdev_exit(void)
{
sigqueue_free(g_sigqueue);
...
}
static ssize_t dev_write(struct file *filep,
const char *buffer,
size_t len,
loff_t *offset)
{
...
memcpy(&g_usp_pid,buffer,4); // we know the PID comes with the buffer
// find task to the given PID
rcu_read_lock();
g_sig_task = pid_task(find_pid_ns(g_usp_pid, &init_pid_ns),
PIDTYPE_PID);
//send signal to user land
g_sigqueue->info.si_value.sival_int = 33;
ret = send_sigqueue(g_sigqueue, g_sig_task, 0);
...
}
Why it isn't linkable? Is my approach wrong?
Any helpful list_head or function that is exported for kernel modules are found as address in the System.map file.
$ sudo grep sigqueue /boot/System.map-3.19.0-58-generic
ffffffff81084880 T sigqueue_alloc
ffffffff810848b0 T sigqueue_free
ffffffff81084920 T send_sigqueue
This function-pointer-addresses can be used to local functions:
#include <linux/init.h> /// Macros used to mark up functions e.g. __init __exit
#include <linux/module.h> /// Core header for loading LKMs into the kernel
#include <linux/device.h> /// Header to support the kernel Driver Model
#include <linux/kernel.h> /// Contains types, macros, functions for the kernel
#include <linux/fs.h> /// Header for the Linux file system support
#include <asm/uaccess.h> /// Required for the copy to user function
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/rcupdate.h>
#include <linux/slab.h>
struct siginfo *g_sig_info;
static struct sigqueue *g_sigqueue;
int g_usp_pid;
struct task_struct *g_sig_task;
// ffffffff81084920 T send_sigqueue
int (*send_sigqueue_ptr)( struct sigqueue *,
struct task_struct *,
int group) = (void*)0xffffffff81084920;
// ffffffff81084880 T sigqueue_alloc
struct sigqueue *(*sigqueue_alloc_ptr)(void) = (void*)0xffffffff81084880;
// ffffffff810848b0 T sigqueue_free
void (*sigqueue_free_ptr)(struct sigqueue *) = (void*)0xffffffff810848b0;
static int __init expdev_init(void)
{
...
// setup signal
g_sigqueue = sigqueue_alloc_ptr();
g_sigqueue->info.si_signo = SIGIO;
g_sigqueue->info.si_code = SI_QUEUE;
g_sigqueue->info.si_errno = 0;
...
}
static void __exit expdev_exit(void)
{
sigqueue_free_ptr(g_sigqueue);
...
}
static ssize_t dev_write(struct file *filep,
const char *buffer,
size_t len,
loff_t *offset)
{
...
memcpy(&g_usp_pid,buffer,4); // we know the PID comes with the buffer
// find task to the given PID
rcu_read_lock();
g_sig_task = pid_task(find_pid_ns(g_usp_pid, &init_pid_ns),
PIDTYPE_PID);
//send signal to user land
g_sigqueue->info.si_value.sival_int = 33;
ret = send_sigqueue_ptr(g_sigqueue, g_sig_task, 0);
...
}
This will work only for this special Kernel version and is not very portable. If there any macro or another workaround do get this addresses or functions? Is it a good way to compile the kernel/signal.c as well?
If anyone knows a way to improve this. Please feel free to comment and edit.

Signal on Kernel parameter change

I want to use parameter inside a kernel (3.x) module:
static char param = 0xff;
module_param(param, ushort, S_IRUGO | S_IWUGO);
MODULE_PARM_DESC(param, "a parameter");
Is there any possibility to detect changes on this parameter? Is there a signal which can use to call a service routine?
Best regards
Alex
Additional information to module_param(name,type,perm):
possible values for type:
bool
invbool
charp
int
long
short
uint
ulong
ushort
defines for perm at linux/stat.h
#define S_IRWXUGO (S_IRWXU|S_IRWXG|S_IRWXO)
#define S_IALLUGO (S_ISUID|S_ISGID|S_ISVTX|S_IRWXUGO)
#define S_IRUGO (S_IRUSR|S_IRGRP|S_IROTH)
#define S_IWUGO (S_IWUSR|S_IWGRP|S_IWOTH)
#define S_IXUGO (S_IXUSR|S_IXGRP|S_IXOTH)
Here a full code example:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Alex");
MODULE_DESCRIPTION("test module.");
MODULE_VERSION("0.1");
static int param = 1;
module_param(param, int, S_IRUGO|S_IWUGO);
static int __init mod_init(void){
printk(KERN_INFO "param %d\n", param);
return 0;
}
static void __exit mod_exit(void){
printk(KERN_INFO "Goodbye!\n");
}
module_init(mod_init);
module_exit(mod_exit);
The compiler returns this error I don't understand why:
make -C /lib/modules/3.19.0-47-generic/build/ M=/tmp/gt modules
make[1]: change to »/usr/src/linux-headers-3.19.0-47-generic«
CC [M] /tmp/gt/ebb.o
In file included from include/linux/thread_info.h:11:0,
from ./arch/x86/include/asm/preempt.h:6,
from include/linux/preempt.h:18,
from include/linux/spinlock.h:50,
from include/linux/seqlock.h:35,
from include/linux/time.h:5,
from include/linux/stat.h:18,
from include/linux/module.h:10,
from /tmp/gt/ebb.c:2:
include/linux/bug.h:33:45: error: negative width in bit-field ‘<anonymous>’
#define BUILD_BUG_ON_ZERO(e) (sizeof(struct { int:-!!(e); }))
^
include/linux/kernel.h:830:3: note: in expansion of macro ‘BUILD_BUG_ON_ZERO’
BUILD_BUG_ON_ZERO((perms) & 2) + \
^
include/linux/moduleparam.h:223:31: note: in expansion of macro ‘VERIFY_OCTAL_PERMISSIONS’
= { __param_str_##name, ops, VERIFY_OCTAL_PERMISSIONS(perm), \
^
include/linux/moduleparam.h:166:2: note: in expansion of macro ‘__module_param_call’
__module_param_call(MODULE_PARAM_PREFIX, name, ops, arg, perm, -1, 0)
^
include/linux/moduleparam.h:146:2: note: in expansion of macro ‘module_param_cb’
module_param_cb(name, &param_ops_##type, &value, perm); \
^
include/linux/moduleparam.h:125:2: note: in expansion of macro ‘module_param_named’
module_param_named(name, name, type, perm)
^
/tmp/gt/ebb.c:11:1: note: in expansion of macro ‘module_param’
module_param(param, int, S_IRUGO|S_IWUGO);
^
make[2]: *** [/tmp/gt/ebb.o] Fehler 1
make[1]: *** [_module_/tmp/gt] Fehler 2
make[1]: leaving »/usr/src/linux-headers-3.19.0-47-generic«
make: *** [all] Fehler 2
and here the Makefile
obj-m+=ebb.o
all:
make -C /lib/modules/$(shell uname -r)/build/ M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build/ M=$(PWD) clean
regarding to the answer from #Tsyvarev
Writing rights are only allowed to root. So here the change:
module_param(param, int, S_IRUGO|S_IWUSR);
Now you can find the parameter under /sys/module//parameters/ and you can change the parameter like this:
sudo echo 2 > /sys/module/ebb/parameters/param
next step regarding to #Tsyvarev
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Alex");
MODULE_DESCRIPTION("test module.");
MODULE_VERSION("0.1");
// int (*set)(const char *val, const struct kernel_param *kp);
// int (*get)(char *buffer, const struct kernel_param *kp);
int my_param_set_ushort(const char *val, const struct kernel_param *kp)
{
unsigned short* pvalue = kp->arg; // Pointer to actual parameter variable.
int res = param_set_ushort(val, kp); // Use helper for write variable
printk(KERN_INFO "setter talks\n");
if( res==0 )
{
// Here you may execute additional actions when you write parameter.
printk(KERN_INFO "set param %d\n", *pvalue);
}
return res;
}
const struct kernel_param_ops my_param_ops_ushort =
{
.set = &my_param_set_ushort, // Use our setter ...
.get = &param_get_ushort, // .. and standard getter
};
unsigned short param = 0xff;
module_param_cb(param, /*filename*/
&my_param_ops_ushort, /*operations*/
&param, /* pointer to variable, contained parameter's value */
S_IRUGO | S_IWUSR /*permissions on file*/
);
static int __init mod_init(void){
printk(KERN_INFO "param %d\n", param);
return 0;
}
static void __exit mod_exit(void){
printk(KERN_INFO "Goodbye! (%d)\n",param);
}
module_init(mod_init);
module_exit(mod_exit);
as root I give this commands:
# insmod par.ko
# echo 146 > /sys/module/par/parameters/param
# rmmod par
and the kernel log /var/log/kernel.log says:
Jan 23 14:27:37 alex-XMG kernel: [ 8332.492912] param 255
Jan 23 14:27:39 alex-XMG kernel: [ 8334.520044] setter talks
Jan 23 14:27:39 alex-XMG kernel: [ 8334.520052] set param 146
Jan 23 14:27:40 alex-XMG kernel: [ 8335.804338] Goodbye! (146)
Works like a charme!
Generic way for create kernel module parameter is using macro module_param_cb:
/**
* module_param_cb - general callback for a module/cmdline parameter
* #name: a valid C identifier which is the parameter name.
* #ops: the set & get operations for this parameter.
* #perm: visibility in sysfs.
*
* The ops can have NULL set or get functions.
*/
#define module_param_cb(name, ops, arg, perm)
Parameter ops is a pointer to structure struct kernel_param_ops, which contains operations for given parameter. Functions called when parameter is written and read have followed definition in this structure:
int (*set)(const char *val, const struct kernel_param *kp);
int (*get)(char *buffer, const struct kernel_param *kp);
Here char* parameter is a NULL-terminated string, which is written to/read from the sysfs file, denoted given parameter. And kp is a pointer to parameter descriptor, where the most interested field is .arg: it is a 3rd argument to macro module_param_cb call. Using this field, setter and getter can be implemented per-type of the module parameter, that is having 5 int parameters for module doesn't require to write setters and getters for each of them.
Moreover, getters and setters for standard parameter types are already implemented, and they are actually used when you call module_param macro. So, if you want to add some functionality for the parameter's setter, you may reuse existed helpers:
int my_param_set_ushort(const char *val, const struct kernel_param *kp)
{
unsigned short* pvalue = kp->arg; // Pointer to actual parameter variable.
int res = param_set_ushort(val, kp); // Use helper for write variable
if(!res)
{
// Here you may execute additional actions when you write parameter.
printk(KERN_INFO "set param %d\n", *pvalue);
}
return res;
}
const struct kernel_param_ops my_param_ops_ushort =
{
.set = &my_param_set_ushort, // Use our setter ...
.get = &param_get_ushort, // .. and standard getter
};
// Usage
unsigned short param = 0xff;
module_param_cb(param, /*filename*/
&my_param_ops_ushort, /*operations*/
&param, /* pointer to variable, contained parameter's value */
S_IRUGO | S_IWUSR /*permissions on file*/
);
Having module parameter's writable by non-priveledged user normally is not good for security reasons. And kernel macros, which create module parameters, check that. That is why you have cryptic error in your module parameter's definition. Note, that in the example above S_IWUSR is used instead of S_IWUGO.
If you make your module parameter writable you can change that parameter via sysfs. Once loaded, find your module under /sys, then find the module parameter and verify that it is writable (by root). Try writing to it and see if it changes.
There are a few ways to detect changes to this variable. You can use a kernel thread as one mechanism.
A better approach might be to use a sysfs or procfs entry. Those have read/write handlers which are called upon reads and writes.
See here for an sysfs-tutorial

Trying to make close sleep on Linux

I need to investigate/test the behavior of some code on Linux under conditions where close might be interrupted by signal handlers (either with or without SA_RESTART). What is the most convenient setup to make the close syscall sleep for a measurable window of time during which I could try to hit the process with a signal? Some ideas:
Intentionally slow/non-responsive NFS mount
Custom FUSE driver
But since these are a bit of a pain to setup, I'm wondering if there's anything more off-the-shelf I could use that could give the desired behavior.
If nobody else has a better idea...
You could implement your own character device driver. Start with the template from Chapter 3 in Linux Device Drivers (3rd edition), and tweak it to do nothing except block for a while on close(). (You can use msleep or msleep_interruptible from Chapter 7 to do the blocking.)
Actually, if nobody else suggests something else, I can probably whip this up pretty quickly by adapting some existing code I have. How soon do you need it?
[edit]
OK, try this...
Makefile:
ifneq ($(KERNELRELEASE),)
obj-m := closer.o
else
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
default: modules
%:
$(MAKE) -C $(KERNELDIR) M=$(PWD) "$#"
.PHONY: default
endif
closer.c:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/delay.h>
#include <linux/fs.h>
MODULE_DESCRIPTION("Block-on-close driver");
MODULE_AUTHOR("Nemo <nemo#self-evident.org>");
MODULE_LICENSE("GPL");
#define VERSION "20110705"
MODULE_VERSION(VERSION);
#define MY_NAME "closer"
int my_open(struct inode *, struct file *);
int my_release(struct inode *, struct file *);
ssize_t my_read(struct file *, char __user *, size_t, loff_t *);
ssize_t my_write(struct file *, const char __user *, size_t, loff_t *);
static struct file_operations my_fops = {
.owner = THIS_MODULE,
.open = my_open,
.read = my_read,
.write = my_write,
.release = my_release,
};
static struct miscdevice my_dev;
int __init
my_init(void)
{
int err = 0;
printk(KERN_INFO "%s: loading version %s\n", MY_NAME, VERSION);
my_dev.minor = MISC_DYNAMIC_MINOR;
my_dev.name = MY_NAME;
my_dev.fops = &my_fops;
err = misc_register(&my_dev);
if (err)
printk(KERN_ERR "%s: misc_register failed, error %d\n", MY_NAME, err);
return err;
}
int
my_open(struct inode *inode, struct file *filp)
{
return 0;
}
ssize_t
my_read(struct file *file, char __user *p, size_t n, loff_t *off) {
return 0;
}
ssize_t
my_write(struct file *file, const char __user *p, size_t n, loff_t *off) {
return n;
}
int
my_release(struct inode *inode, struct file *filp)
{
int err = 0;
/* 10 second sleep, interruptible. */
if (msleep_interruptible(10 * 1000) > 0)
err = -EINTR;
return err;
}
void __exit
my_exit(void)
{
misc_deregister(&my_dev);
printk(KERN_INFO "%s: unloaded\n", MY_NAME);
}
module_init(my_init);
module_exit(my_exit);
Load the module using "insmod closer.o". If you have a reasonably modern/complete Linux environment, udev will wake up and generate /dev/closer automatically. If not, you can create the device node yourself:
mknod /dev/closer c `tr : ' ' </sys/class/misc/closer/dev`
(That is, /sys/class/misc/closer/dev indicates the major:minor to use.)
Reads and writes work like /dev/null; i.e., EOF on any read, success on any write.
I have verified that "cat < /dev/closer" blocks in close() for 10 seconds. I have not created a test to catch SIGINT (or whatever) and verify that it actually results in EINTR.
Built against a 2.6.32 kernel. Let me know how it works for you.

Resources