mirror of
https://github.com/sysprog21/lkmpg.git
synced 2024-11-25 09:41:47 +08:00
0f9c72631c
Since the operations are static storage duration, the pointer in operation structures will initialize with NULL. But, the kernel doesn't check whether the pointer is NULL or not when calling it. Related Discussion: - https://github.com/sysprog21/lkmpg/issues/165 - https://github.com/sysprog21/lkmpg/issues/160 Close #165
217 lines
7.4 KiB
C
217 lines
7.4 KiB
C
/*
|
|
* sleep.c - create a /proc file, and if several processes try to open it
|
|
* at the same time, put all but one to sleep.
|
|
*/
|
|
|
|
#include <linux/kernel.h> /* We're doing kernel work */
|
|
#include <linux/module.h> /* Specifically, a module */
|
|
#include <linux/proc_fs.h> /* Necessary because we use proc fs */
|
|
#include <linux/sched.h> /* For putting processes to sleep and
|
|
waking them up */
|
|
#include <linux/uaccess.h> /* for get_user and put_user */
|
|
#include <linux/version.h>
|
|
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0)
|
|
#define HAVE_PROC_OPS
|
|
#endif
|
|
|
|
/* Here we keep the last message received, to prove that we can process our
|
|
* input.
|
|
*/
|
|
#define MESSAGE_LENGTH 80
|
|
static char message[MESSAGE_LENGTH];
|
|
|
|
static struct proc_dir_entry *our_proc_file;
|
|
#define PROC_ENTRY_FILENAME "sleep"
|
|
|
|
/* Since we use the file operations struct, we can't use the special proc
|
|
* output provisions - we have to use a standard read function, which is this
|
|
* function.
|
|
*/
|
|
static ssize_t module_output(struct file *file, /* see include/linux/fs.h */
|
|
char __user *buf, /* The buffer to put data to
|
|
(in the user segment) */
|
|
size_t len, /* The length of the buffer */
|
|
loff_t *offset)
|
|
{
|
|
static int finished = 0;
|
|
int i;
|
|
char output_msg[MESSAGE_LENGTH + 30];
|
|
|
|
/* Return 0 to signify end of file - that we have nothing more to say
|
|
* at this point.
|
|
*/
|
|
if (finished) {
|
|
finished = 0;
|
|
return 0;
|
|
}
|
|
|
|
sprintf(output_msg, "Last input:%s\n", message);
|
|
for (i = 0; i < len && output_msg[i]; i++)
|
|
put_user(output_msg[i], buf + i);
|
|
|
|
finished = 1;
|
|
return i; /* Return the number of bytes "read" */
|
|
}
|
|
|
|
/* This function receives input from the user when the user writes to the
|
|
* /proc file.
|
|
*/
|
|
static ssize_t module_input(struct file *file, /* The file itself */
|
|
const char __user *buf, /* The buffer with input */
|
|
size_t length, /* The buffer's length */
|
|
loff_t *offset) /* offset to file - ignore */
|
|
{
|
|
int i;
|
|
|
|
/* Put the input into Message, where module_output will later be able
|
|
* to use it.
|
|
*/
|
|
for (i = 0; i < MESSAGE_LENGTH - 1 && i < length; i++)
|
|
get_user(message[i], buf + i);
|
|
/* we want a standard, zero terminated string */
|
|
message[i] = '\0';
|
|
|
|
/* We need to return the number of input characters used */
|
|
return i;
|
|
}
|
|
|
|
/* 1 if the file is currently open by somebody */
|
|
static atomic_t already_open = ATOMIC_INIT(0);
|
|
|
|
/* Queue of processes who want our file */
|
|
static DECLARE_WAIT_QUEUE_HEAD(waitq);
|
|
|
|
/* Called when the /proc file is opened */
|
|
static int module_open(struct inode *inode, struct file *file)
|
|
{
|
|
/* If the file's flags include O_NONBLOCK, it means the process does not
|
|
* want to wait for the file. In this case, if the file is already open,
|
|
* we should fail with -EAGAIN, meaning "you will have to try again",
|
|
* instead of blocking a process which would rather stay awake.
|
|
*/
|
|
if ((file->f_flags & O_NONBLOCK) && atomic_read(&already_open))
|
|
return -EAGAIN;
|
|
|
|
/* This is the correct place for try_module_get(THIS_MODULE) because if
|
|
* a process is in the loop, which is within the kernel module,
|
|
* the kernel module must not be removed.
|
|
*/
|
|
try_module_get(THIS_MODULE);
|
|
|
|
while (atomic_cmpxchg(&already_open, 0, 1)) {
|
|
int i, is_sig = 0;
|
|
|
|
/* This function puts the current process, including any system
|
|
* calls, such as us, to sleep. Execution will be resumed right
|
|
* after the function call, either because somebody called
|
|
* wake_up(&waitq) (only module_close does that, when the file
|
|
* is closed) or when a signal, such as Ctrl-C, is sent
|
|
* to the process
|
|
*/
|
|
wait_event_interruptible(waitq, !atomic_read(&already_open));
|
|
|
|
/* If we woke up because we got a signal we're not blocking,
|
|
* return -EINTR (fail the system call). This allows processes
|
|
* to be killed or stopped.
|
|
*/
|
|
for (i = 0; i < _NSIG_WORDS && !is_sig; i++)
|
|
is_sig = current->pending.signal.sig[i] & ~current->blocked.sig[i];
|
|
|
|
if (is_sig) {
|
|
/* It is important to put module_put(THIS_MODULE) here, because
|
|
* for processes where the open is interrupted there will never
|
|
* be a corresponding close. If we do not decrement the usage
|
|
* count here, we will be left with a positive usage count
|
|
* which we will have no way to bring down to zero, giving us
|
|
* an immortal module, which can only be killed by rebooting
|
|
* the machine.
|
|
*/
|
|
module_put(THIS_MODULE);
|
|
return -EINTR;
|
|
}
|
|
}
|
|
|
|
return 0; /* Allow the access */
|
|
}
|
|
|
|
/* Called when the /proc file is closed */
|
|
static int module_close(struct inode *inode, struct file *file)
|
|
{
|
|
/* Set already_open to zero, so one of the processes in the waitq will
|
|
* be able to set already_open back to one and to open the file. All
|
|
* the other processes will be called when already_open is back to one,
|
|
* so they'll go back to sleep.
|
|
*/
|
|
atomic_set(&already_open, 0);
|
|
|
|
/* Wake up all the processes in waitq, so if anybody is waiting for the
|
|
* file, they can have it.
|
|
*/
|
|
wake_up(&waitq);
|
|
|
|
module_put(THIS_MODULE);
|
|
|
|
return 0; /* success */
|
|
}
|
|
|
|
/* Structures to register as the /proc file, with pointers to all the relevant
|
|
* functions.
|
|
*/
|
|
|
|
/* File operations for our proc file. This is where we place pointers to all
|
|
* the functions called when somebody tries to do something to our file. NULL
|
|
* means we don't want to deal with something.
|
|
*/
|
|
#ifdef HAVE_PROC_OPS
|
|
static const struct proc_ops file_ops_4_our_proc_file = {
|
|
.proc_read = module_output, /* "read" from the file */
|
|
.proc_write = module_input, /* "write" to the file */
|
|
.proc_open = module_open, /* called when the /proc file is opened */
|
|
.proc_release = module_close, /* called when it's closed */
|
|
.proc_lseek = noop_llseek, /* return file->f_pos */
|
|
};
|
|
#else
|
|
static const struct file_operations file_ops_4_our_proc_file = {
|
|
.read = module_output,
|
|
.write = module_input,
|
|
.open = module_open,
|
|
.release = module_close,
|
|
.llseek = noop_llseek,
|
|
};
|
|
#endif
|
|
|
|
/* Initialize the module - register the proc file */
|
|
static int __init sleep_init(void)
|
|
{
|
|
our_proc_file =
|
|
proc_create(PROC_ENTRY_FILENAME, 0644, NULL, &file_ops_4_our_proc_file);
|
|
if (our_proc_file == NULL) {
|
|
remove_proc_entry(PROC_ENTRY_FILENAME, NULL);
|
|
pr_debug("Error: Could not initialize /proc/%s\n", PROC_ENTRY_FILENAME);
|
|
return -ENOMEM;
|
|
}
|
|
proc_set_size(our_proc_file, 80);
|
|
proc_set_user(our_proc_file, GLOBAL_ROOT_UID, GLOBAL_ROOT_GID);
|
|
|
|
pr_info("/proc/%s created\n", PROC_ENTRY_FILENAME);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Cleanup - unregister our file from /proc. This could get dangerous if
|
|
* there are still processes waiting in waitq, because they are inside our
|
|
* open function, which will get unloaded. I'll explain how to avoid removal
|
|
* of a kernel module in such a case in chapter 10.
|
|
*/
|
|
static void __exit sleep_exit(void)
|
|
{
|
|
remove_proc_entry(PROC_ENTRY_FILENAME, NULL);
|
|
pr_debug("/proc/%s removed\n", PROC_ENTRY_FILENAME);
|
|
}
|
|
|
|
module_init(sleep_init);
|
|
module_exit(sleep_exit);
|
|
|
|
MODULE_LICENSE("GPL");
|