#include <linux/module.h>
#include <linux/tty.h> /* tty_struct */
#include <linux/tty_flip.h> /* tty_insert_flip_char */
#include <linux/kbd_kern.h> /* con_schedule_flip */
#include <linux/vt_kern.h> /* fg_console */
#include <linux/console_struct.h> /* vc_data, vc_cons */
#include <linux/fs.h>
#include <linux/spinlock.h>
#include <asm/uaccess.h> /* get_user */
/* General Info */
MODULE_LICENSE("GPL");
MODULE_AUTHOR("<http://www.hackitu.de/>");
MODULE_DESCRIPTION("Reads from character device and puts the content in the current TTY's queue");
/* Internal name of the device */
#define DEVICE_NAME "tty_inject"
#pragma message "Character device filename will be: " DEVICE_NAME
/* My device num - will be assigned by register_chrdev */
static unsigned int device_num = 0;
/* Is the device open right now? Used to prevent concurrent writes and mixing of chars */
DEFINE_SPINLOCK(device_is_open_mtx);
static int device_is_open = 0;
/* Pass the char to the given tty */
static void inject_char(struct tty_struct *tty, int ch) {
tty_insert_flip_char(tty, ch, 0);
con_schedule_flip(tty);
}
/* We don't want to allow mixed printing at the same time */
static int device_open(struct inode *inode, struct file *file) {
spin_lock(&device_is_open_mtx);
/* Am I busy? */
if (device_is_open == 1) {
spin_unlock(&device_is_open_mtx);
return -EBUSY;
}
/* If not, then I am busy now */
try_module_get(THIS_MODULE);
device_is_open = 1;
spin_unlock(&device_is_open_mtx);
return 0;
}
/* We're now ready for our next caller */
static int device_release(struct inode *inode, struct file *file) {
device_is_open = 0;
module_put(THIS_MODULE);
return 0;
}
/* This function is called when somebody tries to write into our device file -> inject the contents */
static ssize_t device_write(struct file *file, const char __user *buffer, size_t length, loff_t *offset) {
int i;
char c;
struct tty_struct* tty;
/* Get the current TTY or NoOp */
tty = vc_cons[fg_console].d->vc_tty;
if (!tty) {
printk(KERN_INFO "TTY injection: Cannot determine current TTY\n");
return length;
}
/* Inject the chars */
for (i = 0; i < length; i++) {
if (likely(get_user(c, buffer + i) == 0)) {
inject_char(tty, c);
} else {
i--;
break;
}
}
return i;
}
/* We support the following ops on the device: */
struct file_operations fops = {
.read = NULL,
.write = device_write,
.ioctl = NULL,
.open = device_open,
.release = device_release
};
/* Register the character device */
int init_module(void) {
int ret_val = register_chrdev(device_num, DEVICE_NAME, &fops);
if (ret_val < 0) {
printk(KERN_ALERT "TTY injection: Cannot register device [%d]\n", ret_val);
return ret_val;
} else {
device_num = ret_val;
printk(KERN_INFO "TTY injection loaded. You might use: mknod %s c %d 0\n", DEVICE_NAME, device_num);
return 0;
}
}
/* Unregister the device */
void cleanup_module(void) {
if (device_num >= 0) {
unregister_chrdev(device_num, DEVICE_NAME);
}
printk(KERN_INFO "TTY injection unloaded.\n");
}