ubuntu reading a file using a loadable module - c

Hi I have a class assignment that requires me to intercept an open call then read the file and edit the output without editing the file itself this is all done in a loadable module in the kernel space. When I say editing I mean like changing the word she to _he replacing the s with an underscore it changes every instance of the word she to _he well that's the desired result. I have looked online for days trying to figure this out I thought I found a suitable example but it kept giving me an error. As soon as I enter the module into the kernel it instantly says killed then it says I cannot remove it because it is in use when it isn't this forces me to restart my virtual machine. Below is the code. Any help would be appreciated thank you.
#include <linux/module.h> // Needed by all modules
#include <linux/kernel.h> // Needed for KERN_INFO
#include <linux/fs.h> // Needed by filp
#include <asm/uaccess.h> // Needed by segment descriptors
int init_module(void)
{
// Create variables
struct file *f;
char buf[128];
mm_segment_t fs;
int i;
// Init the buffer with 0
for(i=0;i<128;i++)
buf[i] = 0;
// To see in /var/log/messages that the module is operating
printk(KERN_INFO "My module is loaded\n");
// I am using Fedora and for the test I have chosen following file
// Obviously it is much smaller than the 128 bytes, but hell with it =)
f = filp_open("/etc/fedora-release", O_RDONLY, 0);
if(f == NULL)
printk(KERN_ALERT "filp_open error!!.\n");
else{
// Get current segment descriptor
fs = get_fs();
// Set segment descriptor associated to kernel space
set_fs(get_ds());
// Read the file
f->f_op->read(f, buf, 128, &f->f_pos);
// Restore segment descriptor
set_fs(fs);
// See what we read from file
printk(KERN_INFO "buf:%s\n",buf);
}
filp_close(f,NULL);
return 0;
}
void cleanup_module(void)
{
printk(KERN_INFO "My module is unloaded\n");
}
module_init(init_module);
module_exit(cleanup_module);

Regarding reading and writing to a file from the kernel, I would suggest that you will read the following article and look into the code snippests in it:
Driving Me Nuts - Things You Never Should Do in the Kernel
Linux Journal 2005, Greg Kroah-Hartman
http://m.linuxjournal.com/article/8110

Related

How to use relative paths in general C program?

I have created a C program which I'd like to publish to Homebrew, so it needs to be able to be run from any working directory (via a symlink in the user's PATH).
The program uses relative paths (relative to the code) for file writing, and I'm not sure how to set it up so that the relative paths will work, regardless of what location the program is run from. (using absolute paths doesn't seem to be an option, since it needs to run on anyone's system).
Please could someone provide some general guidance on how to approach this?
Many thanks in advance.
Under Linux, the file /proc/self/exe is a symbolic link to the actual executable running. Hence you could use that to ascertain the directory where the executable is, and work from there.
For example, this program (executable file /home/pax/testprog):
#include <stdio.h>
#include <unistd.h>
int main(void) {
// Buffer should be more robust in real program but read anyway.
char buff[1000];
ssize_t sz = readlink("/proc/self/exe", buff, sizeof(buff));
// Find final directory separator (from '/home/pax/testprog').
while (sz > 0 && buff[sz-1] != '/')
--sz;
// Truncate string to directory name only, and print.
buff[sz] = '\0';
printf("[%s]\n", buff);
}
generates the following output:
[/home/pax/]
For MacOs, it's the same theory, but you may not necessarily have procfs available to you. On that system however, there's another way to get the path:
int proc_pidpath(
int pid, // pid of the process to know more about
void * buffer, // buffer to fill with the abs path
uint32_t buffersize // size of the buffer
);
So, in order to use this, replace:
ssize_t sz = readlink("/proc/self/exe", buff, sizeof(buff));
with:
ssize_t sz = proc_pidpath(getpid(), buff, sizeof(buff));
though you'll probably also need the libproc.h header (getpid is already included as part of unistd.h).

How to execute a different binary on run-time in Contiki OS?

I am trying to make a sensor that runs on Contiki OS to execute a new binary and replace the current one, if an event occurs.
I am using Cooja simulator and sky note and I uploaded the binary into the coffee file system of the node(using cooja script) and I want to execute the hello-world.ce
To compile my current program(reboot.c) that i will dynamicaly load a module from I used the following commands:
make TARGET=sky clean CLEAN=symbols.?
make reboot.sky TARGET=sky
make CORE=reboot.sky TARGET=sky reboot.sky
And for the hello-world that will be loaded I used:
make TARGET=sky hello-world.ce
Here is part of my code (reboot.c) from where i am trying to execute hello-world
#include "contiki.h"
#include "core/loader/elfloader.h"
#include "cfs/cfs.h"
PROCESS(hello_world_process, "Reboot process");
AUTOSTART_PROCESSES(&hello_world_process);
/*---------------------------------------------------------------------------*/
PROCESS_THREAD(hello_world_process, ev, data)
{
PROCESS_BEGIN();
int i;
int binFile,ret;
elfloader_init();
binFile=cfs_open("hello-world.ce",CFS_READ);
printf("cfs_open:%d\n",binFile); //returns 0 so the file is opened
ret=elfloader_load(binFile);
cfs_close(binFile);
printf("loader returned: %d\n",ret); //returns 0 ->meaning everything is ok
if(ret == ELFLOADER_OK){
printf("elf OK\n");
for(i=0; elfloader_autostart_processes[i] != NULL; i++) {
printf("exec: starting process %s. \n", elfloader_autostart_processes[i]->name);
}
autostart_start(elfloader_autostart_processes);
}
printf("end of rebooting program\n”);
PROCESS_END();
}
It seems that the elfloader_autostart_processes is set to null since the print statement is not executed in the for loop. The program continues and prints "end of rebooting program" and I was expecting it to print hello-world as an indicator that the binary has been loaded and started.
Can you provide any help?
I manage to achieve it using another build in library of Contiki. Specifically I used shell-exec.c that is responsible to execute files and I gave it as an argument the file that I wanted to be executed on run-time. I used a Cooja script to upload the file on the sensor in Cooja simulator.
#include "contiki.h"
#include "shell-exec.c"
#include "serial-shell.h"
#include "cfs/cfs.h"
#include <stdio.h> /* For printf() */
/*---------------------------------------------------------------------------*/
PROCESS(rebooting, "Reboot process");
AUTOSTART_PROCESSES(&rebooting);
/*---------------------------------------------------------------------------*/
PROCESS_THREAD(rebooting, ev, data)
{
PROCESS_BEGIN();
static struct etimer et;
/* Allow some time for the file to upload using cooja. */
etimer_set(&et, 10 * CLOCK_SECOND);
PROCESS_WAIT_UNTIL(etimer_expired(&et));
process_start(&shell_exec_process,"hello-world.ce\0");
PROCESS_END();
}
Cooja Script
WAIT_UNTIL(msg.startsWith('Starting'));
log.log("Mote started\n");
mymote = mote; /* store mote reference */
fs = mote.getFilesystem();
DIR="~/hello-world"; /* Directory that the binary is stored in the host machine running cooja */
mote_file=DIR+ "hello-world.ce";
ret=fs.insertFile(mote_file);
log.log("inserted");

Writing to proc file / give parameter by calling kernel module

I'm supposed to change a configuration parameter of the kernel by using a kernel module. The kernel module should create a proc file and then I should be able to change the parameter by using the cat command, e.g. cat "foobar" > /proc/prompt is supposed to set the parameter to "foobar", where prompt is the name of the proc file that was created in the module.
Furthermore I should be able to initialize the parameter by passing it as an argument when calling the module.
These two articles were basically the only relevant sources that I have found:
http://www.tldp.org/LDP/lkmpg/2.6/html/x769.html for writing to a proc file and http://www.tldp.org/LDP/lkmpg/2.6/html/x323.html for initializing the parameter from the command line.
Now I have a couple of questions, first of all this is the module thus far:
#include <linux/kernel.h>
#include <linux/version.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/proc_fs.h>
#include "sar_main.h"
#define PROCFS_NAME "sarlkm"
char procfs_buffer[PROCFS_MAX_SIZE];
static unsigned long procfs_buffer_size = 0
struct proc_dir_entry *proc_file_entry;
int procfile_read(char *buffer, char **buffer_location, off_t offset, int buffer_length, int *eof, void *data){
int ret;
printk(KERN_INFO "procfile_read (/proc/%s) aufgerufen \n", PROCFS_NAME);
if (offset > 0){
ret = 0;
}
else{
memcpy(buffer, procfs_buffer, procfs_buffer_size);
ret = procfs_buffer_size;
}
return ret;
}
int procfile_write(struct file *file, const char *buffer, unsigned long count, void *data){
procfs_buffer_size = count;
if (procfs_buffer_size > PROCFS_MAX_SIZE){
procfs_buffer_size = PROCFS_MAX_SIZE;
}
if ( copy_from_user(procfs_buffer, buffer, procfs_buffer)){
return -EFAULT;
}
return procfs_buffer_size;
}
static int __init sar_init(void)
{
prompt_proc = create_proc_entry(PROCFS_NAME, 0644, NULL);
if (prompt_proc = NULL){
remove_proc_entry(PROCFS_NAME, &proc_root);
printk(KERN_ALERT "Error: Konnte proc file nicht kreieren")
return -ENOMEM;
}
prompt_proc->read_proc = procfile_read;
prompt_proc->write_proc = procfile_write;
printk(KERN_INFO "proc/%s wurde erfolgreich kreiert", PROCFS_NAME);
return 0;
}
static void __exit sar_cleanup(void)
{
remove_proc_entry(PROCFS_NAME, &proc_root);
printk(KERN_INFO "proc/%s gelöscht", PROCFS_NAME);
}
module_init(sar_init);
module_exit(sar_cleanup);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
I think I should say that I don't really understand why the read and write functions are supposed to work when using the cat command.
My main question is where exactly is the configuration parameter stored in the proc file? If I would write "foobar" to the proc file using cat and then use cat proc/prompt to read the parameter, how does the read function actually get the new value of the parameter, i.e. where is "foobar" stored in the procfile?
If I would try to initialize the parameter using a command line argument I would have to use a global variable in which to store the value of the parameter, but then how could I use that global variable in the read function, so that cat proc/prompt actually gives out the value that was given to the module from the command line?
The cat command internally calls the read() system call to read data from a file (see man strace).
read() passes the arguments to the VFS and the VFS finally calls your custom procfile_read() routine with the passed arguments (and some additional ones passed by the VFS code). If you want to know more about this, look at the fs directory in kernel sources, especially file read_write.c.
Your particular reading function copies, if some conditions are met, the parameter value (which is stored in procfs_buffer to answer one of your questions) into the user-supplied buffer allocated by cat, which is called buffer in your particular code. It is the same one as passed by the read() system call like in:
read(proc_fd, userspace_buf, 10); /* userspace_buf is buffer! */
Error checking omitted for clearness's sake.
To pass the value to the proc file you have two options:
Use module_param() and write it to your buffer; can only be done once because the module is only loadable once (or unload/reload it every time you want to change the parameter but that sounds inconvenient)
Invoke write() from userspace (like in cat) and modify the buffer as often as you want to (this is currently used by your code)
BTW, I really think your reading function should check the pointer to the user data, i.e. use copy_to_user(), not memcpy().
For further information, read Linux Device Drivers. There's only an old edition available at the moment but an updated one is being written.
you can treat xxx_write or xxx_read in driver just as a interface implement,
when you call write or read in user space,
the kernel will invoke xxx_write or xxx_read in kernel space.
so you need to store it yourself when write call,
and fetch them back when read call,
in xxx_write xxx_read

Check if input file is a valid file in C

I am trying to open a file in c using open() and I need to check that the file is a regular file (it can't be a directory or a block file). Every time I run open() my returned file discriptor is 3 - even when I don't enter a valid filename!
Here's what I have
/*
* Checks to see if the given filename is
* a valid file
*/
int isValidFile(char *filename) {
// We assume argv[1] is a filename to open
int fd;
fd = open(filename,O_RDWR|O_CREAT,0644);
printf("fd = %d\n", fd);
/* fopen returns 0, the NULL pointer, on failure */
}
Can anyone tell me how to validate input files?
Thanks!
Try this:
int file_isreg(const char *path) {
struct stat st;
if (stat(path, &st) < 0)
return -1;
return S_ISREG(st.st_mode);
}
This code will return 1 if regular, 0 if not, -1 on error (with errno set).
If you want to check the file via its file descriptor returned by open(2), then try:
int fd_isreg(int fd) {
struct stat st;
if (fstat(fd, &st) < 0)
return -1;
return S_ISREG(st.st_mode);
}
You can find more examples here, (specifically in the path.c file).
You should also include the following headers in your code (as stated on stat(2) manual page):
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
For future reference, here is an excerpt of the stat(2) manpage regarding the POSIX macros available for st_mode field validations:
S_ISREG(m) is it a regular file?
S_ISDIR(m) directory?
S_ISCHR(m) character device?
S_ISBLK(m) block device?
S_ISFIFO(m) FIFO (named pipe)?
S_ISLNK(m) symbolic link? (Not in POSIX.1-1996.)
S_ISSOCK(m) socket? (Not in POSIX.1-1996.)
int isValidFile(char *filename) {
// We assume argv[1] is a filename to open
int fd;
fd = open(filename,O_RDWR|***O_CREAT***,0644);
printf("fd = %d\n", fd);
/* fopen returns 0, the NULL pointer, on failure */
}
you are using 0_CREAT which prompts the function to create if the file doesn't exist.this in the table its number is 3 (0,1,2 being std input std output and std error)
Wrong: check if the file is OK, then if it is, go open it and use it.
Right: go open it. If you can't, report the problem and bail out. Otherwise, use it (checking and reporting errors after each opetation).
Why: you have just checked that a file is OK. That's fine, but you cannot assume it will be OK in 0.000000017 seconds from now. Perhaps the disk wil overheat and break down. Perhaps some other process will mass-delete your entire file collection. Perhaps your cat will trip over the network cable. So let's just check if it's OK again, and then go open it. Wow, what a great idea! No wait...

How can I use Linux's splice() function to copy a file to another file?

here's another question about splice(). I'm hoping to use it to copy files, and am trying to use two splice calls joined by a pipe like the example on splice's Wikipedia page. I wrote a simple test case which only tries to read the first 32K bytes from one file and write them to another:
#define _GNU_SOURCE
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
int main(int argc, char **argv) {
int pipefd[2];
int result;
FILE *in_file;
FILE *out_file;
result = pipe(pipefd);
in_file = fopen(argv[1], "rb");
out_file = fopen(argv[2], "wb");
result = splice(fileno(in_file), 0, pipefd[1], NULL, 32768, SPLICE_F_MORE | SPLICE_F_MOVE);
printf("%d\n", result);
result = splice(pipefd[0], NULL, fileno(out_file), 0, 32768, SPLICE_F_MORE | SPLICE_F_MOVE);
printf("%d\n", result);
if (result == -1)
printf("%d - %s\n", errno, strerror(errno));
close(pipefd[0]);
close(pipefd[1]);
fclose(in_file);
fclose(out_file);
return 0;
}
When I run this, the input file seems to be read properly, but the second splice call fails with EINVAL. Anybody know what I'm doing wrong here?
Thanks!
From the splice manpage:
EINVAL Target file system doesn't support splicing; target file is
opened in append mode; neither of the descriptors refers to a
pipe; or offset given for non-seekable device.
We know one of the descriptors is a pipe, and the file's not open in append mode. We also know no offset is given (0 is equivalent to NULL - did you mean to pass in a pointer to a zero offset?), so that's not the problem. Therefore, the filesystem you're using doesn't support splicing to files.
What kind of file system(s) are you copying to/from?
Your example runs on my system when both files are on ext3 but fails when I use an external drive (I forget offhand if it is DOS or NTFS). My guess is that one or both of your files are on a file system that splice does not support.
The splice(2) system call is for copying between files and pipes and not between files, so it can not be used to copy between files, as has been pointed out by the other answers.
As of Linux 4.5 however a new copy_file_range(2) system call is available that can copy between files. In the case of NFS it can even cause server side copying.
The linked man page contains a full example program.

Resources