can we perform the operation using functions like fgets(), fputs(), feof(),etc. for the fifo file like we use for the normal file? - linux

I have an assignment where I have to transfer the file from a client process to server process using fifo.I have tried to deal with fifo file as the other files we create in the system. It compiled without any error but it didn't execute properly.Can someone please give me an idea about the fifo file structure inside the computer system? What processes and functions are present for it ?Till now, I know how to use create(),read(),write(), open() function for fifo file.Also, I would be grateful if someone could help me to correct my program?
My client and server program are as follows:-
Client Program:-
#include<stdio.h>
#include<string.h>
#include<fcntl.h>
#include<sys/stat.h>
#include<sys/types.h>
#include<unistd.h>
int main()
{
int fd;
char *myfifo ="/tmp/myfifo";
char str[80];
FILE *fp;
char filename[20];
printf("\nEnter filename: ");
gets(filename);
mkfifo(myfifo,0666);
fp = fopen(filename,"r");
if(fp == NULL)
{
printf("\nError opening the file");
exit(1);
}
fd = open(myfifo, O_WRONLY);
while(fgets(str,80,fp)!=NULL)
{
write(fd,str,strlen(str)+1);
}
close(fd);
fclose(fp);
return 0;
}
Client Program:-
#include<stdio.h>
#include<string.h>
#include<fcntl.h>
#include<sys/stat.h>
#include<sys/types.h>
#include<unistd.h>
int main()
{
int fd1;
char *myfifo ="/tmp/myfifo";
char str1[80], filename[20];
FILE *fp1, *fp2;
fd1= open(myfifo, O_RDONLY);
fp1 = fopen(filename,"r");
fp2 = fopen(filename,"w");
while(!feof(fp1))
{
read(fd1,str1,strlen(str1)+1);
fputs(str1,fp2);
}
return 0;
}

Yes, but you have a few small problems in your programs. in the first:
write(fd, str, strlen(str)+1);
is a bit unconventional. This sends the string plus its end-of-string delimiter (\0) into the fd. One doesn't normally do this with strings, strlen(str) is probably what you want.
in the second:
fp1 = fopen(filename,"r");
fp2 = fopen(filename,"w");
filename has not been assigned a value, so both of these opens will almost certainly fail. When they do, they return a NULL pointer, so the first attempt to use them:
while(!feof(fp1))
will likely cause a segment violation. Also, you don't use fp1 anyways, so if feof(fp1) returned 1, it would always return 1. You want to base this loop on when the fifo is exhausted, which means there is no data in it, and nobody has it open for write. So changing this program around a bit yields:
#include<stdio.h>
#include<string.h>
#include<fcntl.h>
#include<sys/stat.h>
#include<sys/types.h>
#include<unistd.h>
int main()
{
int fd1;
char *myfifo ="/tmp/myfifo";
char str1[80];
ssize_t n;
fd1= open(myfifo, O_RDONLY);
while ((n=read(fd1,str1,sizeof str1)) > 0)
{
fwrite(str1, 1, n, stdout);
}
return 0;
}
While this set of changes works, it doesn't address your other question, about using stdio functions with pipes. The answer is yes, and here is another functional rewrite of your second program:
#include<stdio.h>
int main()
{
char *myfifo ="/tmp/myfifo";
FILE *fp;
int c;
if ((fp = fopen(myfifo, "r")) != NULL) {
while ((c = getc(fp)) != EOF) {
putchar(c);
}
fclose(fp);
}
return 0;
}
Also, in the first, the critical bit with stdio:
...
FILE *fi = fopen(myfifo, "a");
while(fgets(str,80,fp)!=NULL)
{
fputs(str, fi);
}
fclose(fi);
...
as in the second, the loop could have been implemented with getc, putc.
A general refinement might be functions like these:
ssize_t FCopy(FILE *in, FILE *out) {
int c;
ssize_t len = 0;
while ((c = getc(in)) != EOF) {
len++;
if (putc(c, out) != c) {
return -len;
}
}
return len;
}
ssize_t FileAppend(char *from, char *to) {
FILE *in, *out;
ssize_t n = 0;
if ((in = fopen(from, "rb")) != NULL) {
if ((out = fopen(to, "ab")) != NULL) {
n = FCopy(in, out);
fclose(out);
} else {
n = -1;
}
fclose(in);
} else {
n = -1;
}
return n;
}
so your main would look more like:
...
char filename[80];
printf("Enter a file to store the data in: ");
if (fgets(filename, sizeof filename, stdin)) {
filename[strlen(filename)-1] = '\0';
if (FileAppend(myfifo, filename) < 0) {
printf("Error: could not save data to %s\n", filename);
}
}
....

Related

why the system hang when I write characters to my dummy character device?

I am learning how to write Linux Device Driver.
I wrote a dummy character device driver, implemented open, release, write, read in fops;
When I read from device , everything was ok;
When I wrote to device by "echo xx > ", the OS was hang.
Even I comment out all codes in write function except pr_alert and return statements, It still hangs;
Could anybody help me figure it out?
#include <linux/init.h>
#include <linux/types.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/string.h>
struct hello_dev
{
char *buffer;
int length;
dev_t dev;
struct mutex lock;
struct cdev *pcdev;
};
struct hello_dev *pHelloDev;
int open_device(struct inode *pinode, struct file *filp)
{
filp->private_data = pHelloDev;
return 0;
}
int close_device(struct inode *pinode, struct file *filp)
{
struct hello_dev *pDev = filp->private_data;
if (pDev->buffer != NULL)
kfree(pDev->buffer);
pDev->buffer = NULL;
return 0;
}
ssize_t read_device(struct file *filp, char __user *buffer, size_t len, loff_t *loff)
{
pr_alert("read\n");
struct hello_dev *pDev = filp->private_data;
mutex_lock(&pDev->lock);
if (pDev->buffer == NULL)
{
mutex_unlock(&pDev->lock);
return 0;
}
int length = strlen(pDev->buffer);
// offset max than strlen in buffer, return
if (*loff > (length - 1))
{
mutex_unlock(&pDev->lock);
return 0;
} else {
// available to read
int len2read = length - *loff;
if (len < len2read)
{// buffer length less than available data
len2read = len;
}
int read = copy_to_user(buffer, pDev->buffer + *loff, len2read);
if (read)
{
*loff = *loff + read;
mutex_unlock(&pDev->lock);
return read;
} else {
*loff = *loff + len2read;
mutex_unlock(&pDev->lock);
return len2read;
}
}
}
ssize_t write_device(struct file *filp , const char __user *buffer, size_t len, loff_t* loff) {
pr_alert("write %s\n", buffer);
// struct hello_dev *pDev = filp->private_data;
// mutex_lock(&pDev->lock);
// if(pDev->buffer == NULL) {
// pDev->buffer = kmalloc(100, GFP_KERNEL);
// pDev->length = 100;
// }
// copy_from_user(pDev->buffer, buffer, len);
// *loff = *loff + len;
// mutex_unlock(&pDev->lock);
return len;
}
struct file_operations fops = {
.open = open_device,
.release = close_device,
.read = read_device,
.write = write_device
};
int init_device(void)
{
pr_alert("init device\n");
pHelloDev = kmalloc(sizeof(struct hello_dev), GFP_KERNEL);
pHelloDev->buffer = NULL;
pHelloDev->length = 0;
int ret = alloc_chrdev_region(&pHelloDev->dev, 0, 1, "hello");
if (ret)
goto alloc_error;
if (pHelloDev == NULL)
goto kmalloc_error;
pHelloDev->pcdev = cdev_alloc();
pHelloDev->pcdev->ops = &fops;
mutex_init(&pHelloDev->lock);
ret = cdev_add(pHelloDev->pcdev, pHelloDev->dev, 1);
if (ret)
goto cdev_add_error;
return 0;
alloc_error:
pr_alert("alloc_chrdev_region error, %d\n", ret);
return ret;
kmalloc_error:
pr_alert("alloc struct hello_dev error");
return -ENOMEM;
cdev_add_error:
pr_alert("cdev_add error, %d\n", ret);
return ret;
}
void cleanup_device(void)
{
pr_alert("unload ko\n");
cdev_del(pHelloDev->pcdev);
unregister_chrdev_region(pHelloDev->dev, 1);
}
MODULE_LICENSE("GPL");
module_init(init_device);
module_exit(cleanup_device);
I found why write to device hangs.
//this statements has problem
//maybe there is no \0 in buffer
//so I print it out, it will hang
//I wrote a program to write something to device
//and used strace to trace system call made by this program
//and found it hangs at write(...) system call
//and there was nothing printed out
//so, it must be this statement causing the problem
//when I removed this statement, everything was ok
pr_alert("write %s\n", buffer);

Named Pipe, Communication between 2 children

I have a problem with my code. I want to make communication between 2 children process. One of them is a server, which opens a file and sends each letter to the second process. The second process is counting letters and it should make a new file and save results. I have problems with the last step because the first process gonna finish faster than the second, what causes the end of the program. I have no idea how fix it. Looking for some tips :).
Here you got result.
My code:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <string.h>
//stale
#define FIFO "my_fifo"
#define SIZE 26
//zmienne globalne
int desk; //deskryptor pliku
int tab[SIZE];
//prototypy funkcji
void parentKillAll();
void server(FILE * file);
void client();
void cleanUp(FILE * file);
int checkEntryData(int argc, char *argv);
void replaceTabWithZero(int * tab);
void countLetters(int * tab, char ch);
void saveResults(int * tab, char *title);
void showTab(int * tab);
int main(int argc, char *argv[]) {
if (!checkEntryData(argc, argv[1]))
return 1;
replaceTabWithZero(tab);
FILE *file = fopen(argv[1], "r");
umask(0);
mkfifo(FIFO, 0666);
if (file) {
if (fork() == 0) {
server(file);
exit(0);
} else if (fork() == 0) {
client();
saveResults(tab, strcat(argv[1], "Result"));
showTab(tab);
exit(0);
} else {
cleanUp(file);
parentKillAll();
}
} else {
perror("Error");
}
return 0;
}
void parentKillAll() {
sleep(1);
kill(0, SIGKILL);
exit(0);
}
void server(FILE * file) {
char ch;
while ((ch = fgetc(file)) != EOF) {
desk = open(FIFO, O_WRONLY);
write(desk, &ch, 1);
}
}
void client() {
char ch;
while (1) {
desk = open(FIFO, O_RDONLY);
read(desk, &ch, 1);
countLetters(tab, ch);
printf("%c", ch);
}
}
void cleanUp(FILE *file) {
wait(0);
fclose(file);
close(desk);
}
int checkEntryData(int argc, char *argv) {
if (argc < 2) {
fprintf(stderr, "Nie poprawna ilosc argumentow\n");
return 0;
}
if (access(argv, F_OK)) {
fprintf(stderr, "Podany plik \'%s\' nie istnieje\n", argv);
return 0;
}
if (access(argv, R_OK)) {
fprintf(stderr, "Brak uprawnien do odczytu pliku \'%s\'\n", argv);
return 0;
}
return 1;
}
void replaceTabWithZero(int * tab) {
for (int i = 0; i < SIZE; i++)
tab[i] = 0;
}
void countLetters(int *tab, char ch) {
int chVal = ch;
if (chVal > 92)
chVal -= 32;
if (chVal > 64 && chVal < 91)
tab[chVal-65] += 1;
}
void saveResults(int *tab, char * title) {
FILE *plik = fopen(title, "w");
if (plik) {
for (int i = 0; i < SIZE; i++)
fprintf(plik, "%c - %d\n", (i+97), tab[i]);
} else {
perror("Error");
}
fclose(plik);
}
void showTab(int * tab) {
for (int i = 0; i < SIZE; i++)
printf("\n%d", tab[i]);
}
The real problem is that the client process can never finish, because it runs an infinite while(1) loop without any exit conditions.
You should rewrite it so that it exits after reading all available data:
void client() {
char ch;
// Open the fifo only once, instead of once per character
desk = open(FIFO, O_RDONLY);
// Loop until there is no more data to read
while(read(desk, &ch, 1) > 0) {
countLetters(tab, ch);
printf("%c", ch);
}
}
This is technically sufficient to make it work, but you should also look into a series of other issues:
You should have two wait(0) calls so that you wait for both processes, and you shouldn't try to kill anything.
The server process should only be opening the fifo once, not once per character.
You should be comparing fgetc output to EOF before forcing the value into a char. Since you do it after, running your program on a ISO-8859-1 terminal will cause it to confuse EOF and the letter ΓΏ
You are using strcat on argv[1], even though you don't know how much space that array has. You should use your own buffer of a known length.
You should check the return value of all your system calls to ensure they succeed. Checking with access and then assuming it'll be fine is not as good since calls can fail for other reasons.
Canonical Unix behavior is to exit with 0 for success, and >= 1 for error.
It's good practice to use a larger buffer (e.g. 65536 bytes instead of 1) when using read/write directly. stdio functions like fgetc already uses a larger buffer behind the scenes.
Using a named pipe obviously works, but since you spawn both processes it would be more natural to use an unnamed one.

TOCTTOU code vulnerability

I have a piece of code in C, and I need to know where I have the TOCTTOU vulnerability and why. Does somebody know where it is and how I can correct it?
int process(char *filename)
{
struct stat aux;
char buffer[1024];
printf("Input to be appended: ");
fgets(buffer, sizeof(buffer), stdin);
if((lstat(filename, &aux) == 0) && !S_ISLNK(aux.st_mode))
{
printf("[+] Opening\n", filename);
int fd = open(filename, O_RDWR | O_APPEND), nb;
nb = write(fd, buffer, strlen(buffer));
printf("[+] Done!\n");
return 0;
}else
printf("[-] ERROR\n", filename);
return 1;
}
int main(int argc, char * argv[])
{
if(argc != 2){
fprintf(stderr, "usage: %s filename\n", argv[0]);
exit(1);
}
return process(argv[1]);
}
Thanks!!
The use of lstat() provides a TOCTOU vulnerability because the file may be deleted after the lstat() and before the open(). Use open() instead and test the return value is a simple solution for this.

Reuse char * pointer needs to free and malloc again?

I would like to implement a main function such as in order to execute system commands. The following code is currently used :
int main(int argc, char *argv[])
{
size_t cmd_length;
char *cmd_buffer = NULL;
char *file = NULL;
char *ip = NULL;
int size;
if(argc == 3)
{
size = strlen(argv[1]);
file = (char*)malloc((size + 1)*sizeof(char));
strcpy(file, argv[1]);
size = strlen(argv[2]);
ip = (char*)malloc((size + 1)*sizeof(char));
strcpy(ip, argv[2]);
}
cmd_length = snprintf(NULL, 0, "tftp -g -r %s %s", file, ip);
cmd_buffer = malloc(cmd_length + 1);
if (cmd_buffer == NULL)
{
return -1;
}
snprintf(cmd_buffer, cmd_length + 1, "tftp -g -r %s %s", file, ip);
if(system(cmd_buffer) == 0)
{
then ...
}
{
return -1;
}
free(cmd_buffer);
cmd_buffer = NULL;
cmd_length = snprintf(NULL, 0, "tftp -g -r %s %s", DFT_FILE, DFT_IP);
cmd_buffer = malloc(cmd_length + 1);
if (cmd_buffer == NULL)
{
return -1;
}
snprintf(cmd_buffer, cmd_length + 1, "tftp -g -r %s %s", DFT_FILE, DFT_IP);
if(system(cmd_buffer) == 0)
{
then ...
}
{
return -1;
}
free(cmd_buffer);
free(file);
free(ip);
cmd_buffer = NULL;
file = NULL;
ip = NULL;
return 0;
}
Because I need to enter other commands, I am currently using the same cmd_buffer by using free() before reallocating memory. Is it the right way to do ? Some other commands might be required in the future.
Your program can be be greatly simplified if you use a common function to execute the system call. It doesn't even need to use malloc at all. Here's a partial implementation [Please pardon the gratuitous style cleanup]:
#include <stdarg.h>
int
execute_command(const char *fmt,...)
{
char cmd_buffer[5000];
int cmd_length;
va_list ap;
// NOTE: much simpler to used a fixed size buffer that is larger than
// needed
va_start(ap,fmt);
cmd_length = vsnprintf(cmd_buffer,sizeof(cmd_buffer),fmt,ap);
va_end(ap);
if (system(cmd_buffer) != 0)
return -1;
return 0;
}
int
main(int argc, char *argv[])
{
char *file = NULL;
char *ip = NULL;
// NOTE: I had to guess the intent if fewer arguments are passed (e.g. just
// skip as I did here, print message and abort?)
if (argc == 3) {
// NOTE: no need to malloc these, but if you did, see strdup(3)
file = argv[1];
ip = argv[2];
execute_command("tftp -g -r %s %s", file, ip);
}
execute_command("tftp -g -r %s %s", DFT_FILE, DFT_IP);
return 0;
}
Yes, you are essentially just re-using the pointer variable cmd_buffer which is fine. And for every malloc() there is a matching free(), which is good.
You should factor our common code into a function, for example runCommand(const char *command, ...) (using varargs).

Error when reading from Linux FIFO

In the embedded application I'm working on we have a serial port abstraction, and I'm currently working on a simulated variant of said abstraction to use when you are not running on the 'real' hardware. I'm using FIFO files for this, as you can then plug in whathever software you want to communicate with the actual application but I'm having trouble with the "read" function, which flags that you gave it an invalid fd. Though I have used debugging tools to verify that the fd passed to it is the same as has been opened earlier so it should be valid. I cannot find any cause for this problem.
FIFO files are opened through this function:
int createOpenFifo(const std::string& path, int flags)
{
int fd = open(path.c_str(), flags);
if (fd < 0)
{
mkfifo(path.c_str(), 0777);
fd = open(path.c_str(), flags);
if (fd < 0)
{
return -1;
}
}
return fd;
}
And the FIFOs are then written to using the following function:
int write_serial(handle_t handle, size_t length, const uint8_t* pData)
{
SerialFifoData* data = static_cast<SerialFifoData*>(handle);
size_t written = 0;
while (written < length)
{
int result = write(data->writeFd, pData + written, length - written);
if (result < 0)
{
return -1;
}
written += result;
}
return 0;
}
And finally read from using this function:
int read_serial(handle_t handle, size_t buffer_size, uint8_t* pBuffer, size_t* bytes_read)
{
SerialFifoData* data = static_cast<SerialFifoData*>(handle);
int return_val = read(data->readFd, pBuffer, buffer_size);
if (return_val < 0)
{
if (errno == EAGAIN || errno == EWOULDBLOCK) // Non-blocking, no data
// which flag is raised
// varies between POSIX
// implementations
{
*bytes_read = 0;
return -2;
}
return -1;
}
*bytes_read = return_val;
return 0;
}
I have verified that each function recieves correct input, and the read and write calls are nearly identical to those used for the actual serial port code (the only difference is how the FD is extracted from the handle) where they work just fine.

Resources