Bad File Descriptor on a serial port - c

I'm implementing a simple protocol to do file transfer between 2 PC's over serial port and I'm getting a weird error.
On the main I call a function "llopen":
int
llopen(int port, int type) {
int fd = 0;
char* PORT;
PORT = malloc( sizeof(char) * (strlen(COM) + 1) );
sprintf(PORT,"%s%d",COM,port);
fd = initialization(PORT); // Open(...): returns a file descriptor!
switch(type) {
case SENDER:
return connectSender(fd);
break;
case RECEIVER:
return connectReceiver(fd);
break;
}
return fd; // The fd value here is 5
}
After that, I call a function llwrite(int fd, ...) to write a string to the file descriptor, but I'm getting an error: "Bad file descriptor" on llwrite(int fd, ...). If I call again the initialization(port) function, before that, it works and it writes the N bytes on the file descriptor, but if I don't it gives to me the "Bad file descriptor" error again.
Here it is the llwrite(int fd, ...) function:
int
llwrite(int fileDescriptor, unsigned char* buffer, unsigned int length) {
// The fd value here is 5
return writeBuffer(fileDescriptor,buffer,length);
}
Even before the return statement if I call, for instance, the tcflush(...) function I'm getting the "Bad file descriptor" error.
Any clue? Thanks in advance!
EDIT:
The problem is solved.
llopen(...) was wrong. I was returning the number of bytes wrote on the ConnectReceiver(...) / ConnectSender(...) and not the file descriptor
Now it's right:
int
llopen(int port, int type) {
int fd = 0;
char* PORT;
PORT = malloc( sizeof(char) * (strlen(COM) + 1) );
sprintf(PORT,"%s%d",COM,port);
fd = initialization(PORT); // Open(...): returns a file descriptor!
switch(type) {
case SENDER:
if( connectSender(fd) > 0 ) return fd;
case RECEIVER:
if( connectReceiver(fd) > 0 ) return fd;
}
return -1;
}

There's not really enough information here, but it's worth a shot noting that you do
return connectSender(fd);
break;
The break there is dead code, since the return stops execution of the function. Perhaps you didn't mean to return?
If that's not the case try using strace to get more details about what's going on. If you're not on linux other OSes should have similar tools, such as dtruss or ktrace.

Related

problem with the fread on TCP server client

I'm building a app TCP server client, i want to send binary to the server/client (so i don't want to use send and recv) and i have a problem, i want to read the file descriptor of my client it's doesn't working i don't understand why because the server accept the client correctly so i have make this function:
bool accept_and_read_client(){
struct sockaddr_in new_client = { 0 };
socklen_t length = sizeof(new_client);
int val_return = accept(sock_fd, (struct sockaddr*)&new_client, &length);
if(val_return < 0){
syserr("error of accept");
return true;
}
printf("accept of client successful\n");
client_array[0].fd = val_return;
size_t nbread;
client_array[0].file = fdopen(client_array[0].fd, "w+");
nbread = fread(&val_return, sizeof(char), sizeof(MAX_BUFF), client_array[0].file);
if(nbread < -1){
syserr("fread");
return true;
}
printf("number i read : %ld\n", nbread);
return false;
}
when i start the server, the server wait client and when i make ./client test the server said:
accept of client successful
fread: Success
so the function fread fails.
on the client.c i do a fwrite like this:
...
FILE* fdc = fdopen(sock_fd, "w+");
if(fdc == NULL){
syserr("error of fdopen");
return 1;
}
printf("%s\n", argv[1]);
size_t nb_write = fwrite(argv[1], sizeof(*argv), strlen(argv[1]), fdc);
printf("number i write: %ld\n", nb_write);
if(fflush(fdc)){
syserr("fflush");
}
if you want to see the client structure:
struct client{
int fd;
// char buffer[MAX_BUFF];
FILE* file;
};
struct client client_array[MAX_CLIENTS];
if somebody have a idea why fread don't work tell me please
The fread function probably doesn't fail, instead it's your check that is flawed:
size_t nbread;
...
if(nbread < -1)
The type size_t is unsigned so when you compare nbread with -1 the value -1 will be converted to an unsigned value as well. And that unsigned value will be very large, so nbread will always be smaller than -1.
The fread function will not return -1 or EOF on error, instead it will return a value smaller than the count argument (the third argument). That's the condition you need to check (nbread < sizeof(MAX_BUFF) with the possibly wrong code you currently show).

Return value from file_operations.write is not respected

I am writing a simple misc device driver for the linux kernel.
In my file_operations.write I do few checks and compare the passed value with a pre defined value, if the value are equal I return the string length, If not I return -EINVAL
The problem is that even that I print the return value before leaving the write, and it is printed in the log as -22, in my client program that I test with I keep getting the count of bytes passed to the write system call. !
Below is a sample from my write function:
ssize_t misc_write(struct file *filp, const char __user *buff,
size_t count, loff_t *offp)
{
ssize_t retval;
pr_crit("count: %zu\n", count);
pr_crit("strlen(MY_UNIQUE_ID) + 1: %zu\n", strlen(MY_UNIQUE_ID) + 1);
printk(KERN_INFO "Inside write \n");
if (count != (strlen(MY_UNIQUE_ID) + 1)) {
retval = - EINVAL;
pr_crit("retval: %i\n", retval);
goto out;
}
if (strncmp(MY_UNIQUE_ID, buff, count))
retval = -EINVAL;
else
retval = count;
out:
pr_crit("retval: %i\n", retval);
return retval;
}
Below is my test client:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char **argv)
{
char buffer[] = "0daf007211a9";
char filename[] = "/dev/misctest";
int string_size, write_size;
FILE *handler = fopen(filename, "r+");
if (handler == 0)
{
printf("error openning file\n");
return -1;
}
write_size = fwrite(&buffer, sizeof(char), 2, handler);
if (write_size < 0)
printf("Error");
printf("write_size: %i\n", write_size);
return 0;
}
And this is what is printed in the kernel logs:
[793868.964583] count: 2
[793868.964593] strlen(MY_UNIQUE_ID) + 1: 13
[793868.964596] Inside write
[793868.964600] retval: -22
[793868.964602] retval: -22
When testing kernel stuff, always use as low level user space api as possible. If you were using write() (the system call) everything would be fine (you will get your error code out). But you decided to go with the more complicated fwrite() function which does something different (http://linux.die.net/man/3/fwrite):
On success, fread() and fwrite() return the number of items read or
written. This number equals the number of bytes transferred only when
size is 1. If an error occurs, or the end of the file is reached, the
return value is a short item count (or zero).
fread() does not distinguish between end-of-file and error, and callers
must use feof(3) and ferror(3) to determine which occurred.
In fact, fwrite() could not possibly return a negative value even if it wanted too (check out its signature).

C (Linux) Checking socket

I'm writting a client-server C project where the server processes messages from the client, and replicates them to a backup server. It works well, but one of the points of the project (its a university project) is to be prepared for errors, in this case - the backup server dying.
I have this on my replication function:
int table_skel_replicate(struct message_t *msg, int sockSecundario){
char *msg_buf;
int buf_size_net = 0;
int buf_size=0;
int okk;
struct sockaddr_in server;
msg->opcode--;
buf_size = message_to_buffer ( msg, &msg_buf );
buf_size_net = htonl(buf_size);
okk = write( sockSecundario, &buf_size_net, sizeof(int) );
if(okk == -1 || okk == 0) {
msg->opcode++;
return okk;
}
okk = write_all ( sockSecundario, msg_buf, buf_size );
if(okk == -1) {
msg->opcode++;
return okk;
}
okk = read ( sockSecundario, &buf_size_net, sizeof(int) );
if(okk == -1 || okk == 0) {
msg->opcode++;
return okk;
}
buf_size = ntohl(buf_size_net);
msg_buf = malloc ( buf_size );
okk = read_all( sockSecundario, msg_buf, buf_size );
msg->opcode++;
return okk;
}
it was supposed to fail the first write() as soon as the backup server died. but it doesnt.
Instead, it proceds to the write_all() function (which is basicly a write() inside a while for long transfers) and dies on the first write() there. i tried using the checksockopt() function but it return the status as ok...
The main application simply closes. Any help appreciated...
Your checks of the write() calls presumably are not effective because the program terminates due to SIGPIPE. man 2 write:
fd is connected to a pipe or socket whose reading end is
closed. When this happens the writing process will also receive a
SIGPIPE signal. (Thus, the write return value is seen only if
the program catches, blocks or ignores this signal.)
To easily avoid this, replace every
write( sockSecundario, …, … )
by
send(sockSecundario, …, …, MSG_NOSIGNAL)

Send a File with socket in C for Linux

I'm writing a small and simple server (in C language for Linux stations).
A client requests a file to my server, my server asks this file to another server which sends it to my server.
My server should NOT receive ALL the file before sending it to the client BUT must send the bytes of the file so as they arrive.
This is an exercise in school so I can not dissociate myself from this requirement.
I have implemented the function explained below. The problem is that the client receives a non-deterministic number of bytes and NEVER the entire file.
int Recv_and_send_file (int socketa, int socketb, char *buffer, size_t file_size){
size_t n;
ssize_t nread;
ssize_t nwritten;
char c;
for (n=1; n<file_size; n++)
{
nread=recv(socketa, &c, 1, 0);
if (nread == 1)
{
nwritten = send(socketb,&c,1,0);
}
else if (nread == 0)
{
*buffer = 0;
return (-1); /* Errore */
}
else
return (-1); /* Errore */
}
}
*buffer = 0;
return (n);
}
Someone could kindly tell me where I'm wrong?
Is it an stupid idea to change the values ​​SO_SNDBUF and SO_RCVBUF on both the server and the client?
Assuming the file_size is the total number of bytes you want to send, then your for loop will only send file_size - 1 bytes. In other words, you are off by one. Start from 0 instead to fix this:
for (n=0; n<file_size; n++)
{ //..
You capture the return value of send(), but you do not check to see if it was successful or not.
You are treating a 0 return value from recv() the same as an error. Since you do not show what you do after returning -1 from your function, I don't know if this may be contributing to your problem or not.
Certain errors on send() and recv() are "soft", in that you are allowed to retry the operation for those particular errors. One such error is EINTR, but check the documentation on your system to see if there are others.
In order to optimize performance and simplify your code, you can use splice()+pipes. Sendfile enables you to "forward" data between file descriptors, without the copy to user space.
Are you sure you have copied the correct code? That part as it is would not compile, there is a } in the last else which don't match with a corresponding {.
Also, how you get to know the file size? if it's send thru the socket as an integer, bear in mind the possible byte order of the source and destination machines.
Anyway, you are reading one byte at a time, you should improve it this way:
EDIT: use buffer and not the extra buff[2048];
int Recv_and_send_file (int socketa, int socketb, char *buffer, size_t file_size){
ssize_t nread;
ssize_t nwritten;
ssize_t bLeft=file_size;
while (bLeft > 0)
{
nread=recv(socketa, buffer, bleft, 0);
if (nread > 0)
{
nwritten = send(socketb, buffer, nread, 0);
bLeft -= nread;
buffer+=nread;
}
else if (nread == 0)
{
// I think this could raise a memory exception, read below
*buffer = 0;
return (-1); /* Errore */
}
else
{
return (-1); /* Errore */
}
}
// If buffer is allocated with file_size bytes this one will raise a memory exception
// *buffer = 0;
return (file_size-bLeft);
}

ioctl - invalid argument

I have
#define IOCTL_ALLOC_MSG _IO(MAJOR_NUM, 0)
#define IOCTL_DEALLOC_MSG _IO(MAJOR_NUM, 1)
in a header file.
and in the driver file I wrote:
struct file_operations memory_fops = {
unlocked_ioctl: device_ioctl,
open: memory_open,
release: memory_release
};
int memory_init(void) {
int result;
/* Registering device */
result = register_chrdev(MAJOR_NUM, "memory", &memory_fops);
if (result < 0) {
printk("<1>memory: cannot obtain major number %d\n", MAJOR_NUM);
return result;
}
allocfunc();
printk("<1>Inserting memory module\n");
return 0;
}
int device_ioctl(struct inode *inode, /* see include/linux/fs.h */
struct file *file, /* ditto */
unsigned int ioctl_num, /* number and param for ioctl */
unsigned long ioctl_param)
{
/*
* Switch according to the ioctl called
*/
printk ( "<l> inside ioctl \n" );
switch (ioctl_num) {
case IOCTL_ALLOC_MSG:
allocfunc();
break;
case IOCTL_DEALLOC_MSG:
deallocfunc();
break;
}
return 0;
}
I created the character file like
mknod /dev/memory c 60 0
the app call fails
int main(int argc, char *argv[]) {
FILE * memfile;
/* Opening the device parlelport */
memfile=fopen("memory","r+");
if ( memfile <0) {
printf ( " cant open file \n");
return -1;
}
/* We remove the buffer from the file i/o */
int ret_val;
if ( argc > 1 ) {
if ( strcmp (argv[1], "mem" ) ==0 ) {
ret_val = ioctl(memfile, IOCTL_ALLOC_MSG);
if (ret_val < 0) {
printf("ioctl failed. Return code: %d, meaning: %s\n", ret_val, strerror(errno));
return -1;
}
}
when i run the app i get "ioctl failed. Return code: -1, meaning: Invalid argument" in : strerror(errno)
printk:
Inserting memory module
fyi, I experimented with "/dev/memory" "memory" different names and major number combinations - but in vain.
You are passing a FILE* to the ioctl() function, while it expects a file descriptor, that is an int.
It should at the very least generate a big warning saying that you are converting a pointer to integer without a cast, doesn't it?
There are two obvious solutions:
Use the fileno() function to get the file descriptor from the FILE*. It should be something like ioctl(fileno(memfile), IOCTL_ALLOC_MSG).
Use open() instead of fopen(). This one is the preferred solution if you are writing low level code, as you avoid the additional abstraction layer that FILE* imposes (all the buffering stuff and so).
I'm going to postulate that changing fopen("memory") to fopen("/dev/memory1") will fix the initial problem with your code.
#SunEric also points out in a comment on your question that you have a call to allocFunc() in your driver's initialization function (memory_init()), and yet that seems to be what you want your IOCTL_ALLOC_MSG to do. That may well be the next problem you need to straighten out.

Resources