New to C and Kernel Programming - so wondering if you can advise what I need to change to get the correct output.
I have a kernel module, which will create /proc/test_file
When I use the cat command on /proc/test_file - I want it to return the integer value stored in a variable "my_number". but right now it is returning the ascii character * (ascii decimal for * is 42), not the integer I have stored in "my_number". I'm assuming I'm calling the function incorrectly, or passing the variable in incorrectly somehow. I don't really get C yet.
// Includes
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/proc_fs.h>
// License
MODULE_LICENSE("GPL");
// Defines
#define TEST_FILE "test_file"
// Declarations & Variables
static int start_module(void);
static void exit_module(void);
static void create_proc_fs_file(void);
static void cleanup_proc_fs_file(void);
static struct proc_dir_entry *res;
static int my_number = 42;
static ssize_t read_proc(struct file *file, char __user *buffer, size_t count, loff_t *pos);
static struct proc_ops test_fops = {
.proc_read = read_proc
};
// Entry and Exit
module_init(start_module);
module_exit(exit_module);
// Code
static int start_module(void)
{
pr_info("\n\nSTARTING PROC ENTRY CREATION\n\n");
create_proc_fs_file();
return 0;
}
static void exit_module(void)
{
cleanup_proc_fs_file();
pr_info("\n\nExiting Module. Bye Bye!\n\n");
}
static void create_proc_fs_file(void)
{
// Create /proc/test_file
res = proc_create(TEST_FILE, 0666, NULL, &test_fops);
if(!res){
pr_err("\nFailed to create /proc/test_file\n\n");
}else{
pr_info("\nSuccessfully create /proc/test_file\n\n");
}
}
static void cleanup_proc_fs_file(void)
{
pr_info("\nRemoving /proc/test_file subtree....\n\n");
remove_proc_subtree(TEST_FILE, NULL);
}
static ssize_t read_proc(struct file *file, char __user *buffer, size_t count, loff_t *pos)
{
size_t len = sizeof(my_number);
pr_info("\nproc file read....\n\n");
if (*pos >= len) {
return 0; // End of file
}
if (count > len - *pos) {
count = len - *pos;
}
if(copy_to_user(buffer, &my_number + *pos, count)){
return -EFAULT;
}
*pos += count;
return count;
}
Expected Output:
chris@dev:~$cat /proc/test_file
42
Actual Output:
chris@dev:~$cat /proc/test_file
*
I'm guessing the problem is here, that i'm telling it that "42" is a char and it's converting it or something:
static ssize_t read_proc(struct file *file, char __user *buffer, size_t count, loff_t *pos)
Edit:
Just to come back and add working code based on the answer from @Tsyvarev below. One of the benefits is that it bypasses the need for file_operations via proc_ops - and so it will work on older kernel releases now (if that's something you need...)
// Includes
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
// License
MODULE_LICENSE("GPL");
// Defines
#define TEST_FILE "test_file"
// Declarations & Variables
static int start_module(void);
static void exit_module(void);
static void create_proc_fs_file(void);
static void cleanup_proc_fs_file(void);
static struct proc_dir_entry *res;
static int my_number = 42;
static int print_out(struct seq_file *m, void *v);
// Entry and Exit
module_init(start_module);
module_exit(exit_module);
// Code
static int start_module(void)
{
pr_info("\n\nSTARTING PROC ENTRY CREATION\n\n");
create_proc_fs_file();
return 0;
}
static void exit_module(void)
{
cleanup_proc_fs_file();
pr_info("\n\nExiting Module. Bye Bye!\n\n");
}
static void create_proc_fs_file(void)
{
// Create /proc/test_file
res = proc_create_single(TEST_FILE, 0666, NULL, &print_out);
if(!res){
pr_err("\nFailed to create /proc/test_file\n\n");
}else{
pr_info("\nSuccessfully create /proc/test_file\n\n");
}
}
static void cleanup_proc_fs_file(void)
{
pr_info("\nRemoving /proc/test_file subtree....\n\n");
remove_proc_subtree(TEST_FILE, NULL);
}
static int print_out(struct seq_file *m, void *v)
{
seq_printf(m, "Count of Packets: %d\n", my_number);
return 0;
}
seq_file
provides a convenient mechanism which allows to easily create a file, representing some information as a string. For procfs this mechanism is available e.g. via proc_create_single
function:
// Pointer to the created file, so it can be deleted in the exit function.
static struct proc_dir_entry *res;
// Number which will be represented by the file.
static int my_number = 42;
// Generates the content of the file
static int my_show(struct seq_file *m, void *v)
{
// Just "print" the value of the integer variable.
// Tail '\n' is useful for pretty output when use 'cat' utility.
seq_printf(m, "%d\n", my_number);
return 0;
}
static int start_module(void)
{
pr_info("\n\nSTARTING PROC ENTRY CREATION\n\n");
// Create a file
res = proc_create_single(TEST_FILE, 0666, NULL, &my_show);
// ... check for errors
return 0;
}
With that approach you just provide show
function (in the example above it is named as my_show
) which will be called whenever the file will be read. The purpose of that function is to generate content, which will be returned to the user.
There are several helpers for generate content in such function, and seq_printf
is one of them. As you could guess from its name, this helper is very similar to printf
function, but operates with a seq_file.
The show
function is then passed to proc_create_single
as a parameter.
The seq_file
mechanism is described e.g. in the kernel documentation. That documentation describes only appliance of the mechanism to "standard" files, with file_operations
. Adaptation of the mechanism to procfs files is represented by several functions in include/linux/proc_fs.h header.