6. Proc Interface

6.1. Introduction

We have inserted modules to the kernel and can get them do something useful. But how to get some information from the kernel.

We need to be able to get some information from the kernel. There are multiple ways of doing it.

  • System Calls
  • Proc File System and Sysfs file system
  • Netlink Kernel Sockets

In this chapter we will see the Proc Interface. Others are not in the scope of this book.

6.1.1. Why this chapter

This chapter will enable you to understand and write your own proc interface. Proc interface is being used by kernel to communicate information to the userspace and take input from the userspace. For example: file /proc/mounts is a file which lists the file systems which are mounted on the system.

Proc insterface is useful to communicate some information to the kernel space as well. For example the file /proc/sysrq-trigger gives you ability to ask the kernel to behave differently. For example

echo l > /proc/sysrq-trigger will print the backtraces of all cpus. The file linux-4.7/Documentation/sysrq.txt describes this in more detail.

For more details on proc interface read https://www.kernel.org/doc/Documentation/filesystems/proc.txt.

Thus - we can also use the proc interface to talk to the user space. In the next chapter on BUGS, we will see how a write to a proc interface can trigger different function calls in the kernel space. In the next chapter on linked list, we will see how we can get some work done using the proc interface. Understanding the code of this chapter is thus important as it becomes the ground work of next chapters.

6.1.2. What will you learn

  • Writing a proc interface.

6.1.3. Concepts and Keywords involved

6.1.4. Prerequisites

None

6.2. New Functions

6.2.1. kmalloc()

  • kmalloc() is the kernel space version of malloc()
  • call it kmalloc(bytes, GFP_KERNEL)
  • Read more about it in Linux Kernel Develpement, Robert Love

6.3. Proc entry to read and write data to it

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
/*
 * Module to create a proc entry. The user can read and write to the proc entry.
 */

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/proc_fs.h>
#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <asm/uaccess.h>
#include <asm/types.h>

#define DATA_SIZE 1024 // We can keep 1024 bytes of data with us.
#define MY_PROC_ENTRY "my_proc_entry_write"
#define PROC_FULL_PATH "/proc/my_proc_entry_write"

struct proc_dir_entry *proc;
int len;
char *msg = NULL;

/*
 * Function to write to the proc. Here we free get the new value from buffer,
 * count from the buffer and then overwrite the data in our file.
 *
 * Note that - you can have any other implementation as well for this, all you have to
 * ensure that you comply with the expectations of the write() system calls
 * like filling in the buffer, and returning the numbers of character written.
 */

static ssize_t my_proc_write(struct file *filp, const char __user * buffer, size_t count, loff_t *pos)
{
    int i;
    char *data = PDE_DATA(file_inode(filp));

    if (count > DATA_SIZE) {
        return -EFAULT;
    }

    printk(KERN_INFO "Printing the data passed. Count is %lu", (size_t) count);
    for (i=0; i < count; i++) {
        printk(KERN_INFO "Index: %d . Character: %c Ascii: %d", i, buffer[i], buffer[i]);
    }

    printk(KERN_INFO "Writing to proc");
    if (copy_from_user(data, buffer, count)) {
        return -EFAULT;
    }

    data[count-1] = '\0';

    printk(KERN_INFO "msg has been set to %s", msg);
    printk(KERN_INFO "Message is: ");
    for (i=0; i < count; i++) {
        printk(KERN_INFO "\n Index: %d . Character: %c", i, msg[i]);
    }

    *pos = (int) count;
    len = count-1;

    return count;
}

/*
 * Function to read the proc entry, here we copy the data from our proc entry 
 * to the buffer passed.
 */

ssize_t my_proc_read(struct file *filp,char *buf, size_t count, loff_t *offp )
{
    int err;
    char *data = PDE_DATA(file_inode(filp));

    if ((int) (*offp) > len) {
        return 0;
    }

    printk(KERN_INFO "Reading the proc entry, len of the file is %d", len);
    if(!(data)) {
        printk(KERN_INFO "NULL DATA");
        return 0;
    }

    if (count == 0) {
        printk(KERN_INFO "Read of size zero, doing nothing.");
        return count;
    } else {
        printk(KERN_INFO "Read of size %d", (int) count);
    }

    count = len + 1; // +1 to read the \0
    err = copy_to_user(buf, data, count); // +1 for \0
    printk(KERN_INFO "Read data : %s", buf);
    *offp = count;

    if (err) {
        printk(KERN_INFO "Error in copying data.");
    } else {
        printk(KERN_INFO "Successfully copied data.");
    }

    return count;
}


/*
 * The file_operations structure. This is the glue layer which associates the
 * proc entry to the read and write operations.
 */
struct file_operations proc_fops = {
    .read = my_proc_read,
    .write = my_proc_write,
};


/*
 * This function will create the proc entry. This function will allocate some
 * data where the data will be written incase of a write to the proc entry. The
 * same memory will be used to serve the reads.  * Initially the function fills
 * the data with DATA which has "Hello People".

 * The important function to see here is the proc_create_data, this function
 * will take the proc entry name and create it with the given permissions
 * (0666). We also need to pass the file_operations structure which has the
 * function pointers to the functions which needs to be called when read or
 * write is called on the file. The last argument has the pointer to the data
 * associated with the file.
 */

int create_new_proc_entry(void) {
    int i;
    char *DATA = "Hello People";
    len = strlen(DATA);
    msg = kmalloc((size_t) DATA_SIZE, GFP_KERNEL); // +1 for \0

    if (msg != NULL) {
        printk(KERN_INFO "Allocated memory for msg");
    } else {
        return -1;
    }

    strncpy(msg, DATA, len+1);
    for (i=0; i < len +1 ; i++) {
        printk(KERN_INFO "%c", msg[i]);
        if (msg[i] == '\0') {
            printk(KERN_INFO "YES");
        }
    }
    proc = proc_create_data(MY_PROC_ENTRY, 0666, NULL, &proc_fops, msg);
    if (proc) {
        return 0;
    }
    return -1;
}


/* The init function of the module. Does nothing other than calling the
 * create_new_proc_entry. */

int proc_init (void) {
    if (create_new_proc_entry()) {
        return -1;
    }
    return 0;
}

/* Function to remove the proc entry.  Call this when the module unloads. */
void proc_cleanup(void) {
    remove_proc_entry(MY_PROC_ENTRY, NULL);
}

MODULE_LICENSE("GPL");
module_init(proc_init);
module_exit(proc_cleanup);
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
MYPROC=myproc
obj-m += $(MYPROC).o

export KROOT=/lib/modules/$(shell uname -r)/build
#export KROOT=/lib/modules/$(uname)3.2.0-23-generic/build

allofit:  modules

modules: clean

	@$(MAKE) -C $(KROOT) M=$(PWD) modules

modules_install:
	@$(MAKE) -C $(KROOT) M=$(PWD) modules_install

kernel_clean:
	@$(MAKE) -C $(KROOT) M=$(PWD) clean

clean: kernel_clean
	rm -rf   Module.symvers modules.order

insert: modules
	sudo dmesg -c
	sudo insmod myproc.ko

remove: clean
	sudo rmmod myproc

        
  • Let us first insert the module by running make insert
  • Let us print the original value.
cat /proc/my_proc_entry_write
Hello People$
  • Let us now write on the original value.
# echo "I am writing this" > /proc/my_proc_entry_write
# cat /proc/my_proc_entry_write
I am writing this