I am using Ubuntu 20.04.3 on a Oracle Virtual Box.
I have few calls to printf() functions that use file descriptors,
but get segmentation fault after the second call.
I tried fsync(fd) and fdatasync(fd) but it didn't solve the problem:
logFileFd = open("./logFile.log", O_APPEND);
if (logFileFd == -1)
{
printf("\ncan't create file %s :[%s]", logFileName, strerror(err));
exit(ERROR_OPEN_LOG_FILE);
}
va_start(a_list, format);
sprintf(logStr, "%s, %s, INFO, %s, %d - ", __DATE__, __TIME__, __BASE_FILE__, __LINE__);
printf("%s", logStr);
dprintf(logFileFd, "%s", logStr);
vprintf(format, a_list);
vdprintf(logFileFd, format, a_list);
va_end(a_list);
close(logFileFd);
the segmentation fault occurs at the line:
vdprintf(logFileFd, format, a_list);
Can any one assist why is that?
Thanks a lot !
Quoting man page for vfprintf(3):
The functions vprintf(), vfprintf(), vsprintf(), vsnprintf() are
equivalent to the functions printf(), fprintf(), sprintf(),
snprintf(), respectively, except that they are called with a va_list
instead of a variable number of arguments. These functions do not call
the va_end macro. Because they invoke the va_arg macro, the value of
ap is undefined after the call.
Additionally on the man page for stdarg(3) you can read that:
If ap is passed to a function that uses va_arg(ap,type) then the value
of ap is undefined after the return of that function.
The problem in your code is that you are using va_list a_list twice - first in call to vprintf(), then in call to vdprintf(). After the first call, a_list value is undefined.
Man stdarg(3) states that "Multiple traversals of the list, each bracketed by va_start() and va_end() are possible". Try applying following modification:
sprintf(logStr, "%s, %s, INFO, %s, %d - ", __DATE__, __TIME__, __BASE_FILE__, __LINE__);
printf("%s", logStr);
dprintf(logFileFd, "%s", logStr);
va_start(a_list, format);
vprintf(format, a_list);
va_end(a_list);
va_start(a_list, format);
vdprintf(logFileFd, format, a_list);
va_end(a_list);
Additionally, please review the flags used in call to open(). According to man page open(2):
The argument flags must include one of the following access
modes: O_RDONLY, O_WRONLY, or O_RDWR
I would propose to apply following modification:
logFileFd = open("./logFile.log", O_WRONLY | O_APPEND);
Thank you (ytta) very very much for your prompted help.
I implemented all your answer and suggestions and it worked perfectly.
The only thing I needed to add (which was not in my question) is the mode_t to the open() function and use the it as:
*int open(const char pathname, int flags, mode_t mode);
setting mode to 0777
because when I added the O_CREAT bit: open("./logFile.log", O_APPEND | OCREAT);
the file logFile.log was created with the sticky bit ON (enabled) in the file system, and in the next iteration the open() failed.
Thanks again very much for your help.
Related
I'm implementing a file in /proc which I'd like to be a little more file-like than usual. In particular, I'd like to detect that someone is appending to the file and handle that correctly -- that is, I'd like to distinguish between someone doing
echo value > /proc/my_proc_file
and
echo value >> /proc/my_proc_file
Like all write functions, mine is handed an offset as its fourth argument:
ssize_t my_proc_write(struct file *file, const char __user *buf,
size_t count, loff_t *offs)
But *offs is always 0.
When I set up my proc file, I'm specifying seq_lseek as the lseek function:
struct file_operations my_proc_fops = {
.open = my_proc_open,
.read = seq_read,
.write = my_proc_write,
.llseek = seq_lseek,
};
Inspecting the source (in fs/seq_file.c), it looks like seq_lseek maintains file->f_pos appropriately, but when I look at file->f_pos in my write function, it's always 0, too. (This may not be surprising, since appending usually means opening with O_APPEND which doesn't result in any explicit calls to lseek.)
Anyway, is there a way to do this? Presumably these write functions wouldn't have been set up with offset pointer arguments if they weren't going to pass in useful, nonzero values from time to time...
first, from user perspective, file opened with O_APPEND will ALWAYS append data when you call write(), no matter where the f_pos is set by llseek(). but f_pos is still effective for read().
second, kernel framework dosn't know the file length unless it calls your llseek to find out, but that's not gonna happen because it will mess up f_pos,so kernel expect your driver, which is the only one who knows where is the true "end of the file", to act accordingly when (file->f_flags & O_APPEND) is true. basically, your driver needs to check that flag when write() is called and ignore the offs param and do the append.
I'm writing a program that must take user input to assign values to parts of a structure. I need to create a pointer to the structure that I will pass through as a one and only parameter for a function that will print each part of the structure individually. I also must malloc memory for the structure. As it is now, the program compiles and runs through main and asks the user for inputs. A segmentation fault occurs after the last user input is collected and when I'm assuming the call to the printContents function is run. Any help would be appreciated!
#include <stdio.h>
#include <stdlib.h>
struct info
{
char name[100], type;
int size;
long int stamp;
};
void printContents(struct info *iptr);
int main(void)
{
struct info *ptr=malloc(sizeof(struct info));
printf("Enter the type: \n");
scanf("%c", &(*ptr).type);
printf("Enter the filename: \n");
scanf("%s", (*ptr).name);
printf("Enter the access time: \n");
scanf("%d", &(*ptr).stamp);
printf("Enter the size: \n");
scanf("%d", &(*ptr).size);
printf("%c", (*ptr).type);
printContents(ptr);
}
void printContents(struct info *iptr)
{
printf("Filename %s Size %d Type[%s] Accessed # %d \n", (*iptr).name, (*iptr).size, (*iptr).type, (*iptr).stamp);
}
Check the operator precedence. Is this &(*ptr).type the thing you're trying to do? Maybe &((*ptr).type) ?
ptr->member is like access to structure variable right? Also same for scanf() usr &ptr->member to get value. For char input use only ptr->charmember .
First let's do it the hard way. We'll assume that the code is already written, the compiler tells us nothing useful, and we don't have a debugger. First we put in some diagnostic output statements, and we discover that the crash happens in printContents:
printf("testing four\n"); /* we see this, so the program gets this far */
printf("Filename %s Size %d Type[%s] Accessed # %d \n", (*iptr).name, (*iptr).size, (*iptr).type, (*iptr).stamp);
printf("testing five\n"); /* the program crashes before this */
If we still can't see the bug, we narrow the problem down by preparing a minimal compete example. (This is a very valuable skill.) We compile and run the code over and over, commenting things out. When we comment something out and the code still segfaults, we remove it entirely; but if commenting it out makes the problem go away, we put it back in. Eventually we get down to a minimal case:
#include <stdio.h>
int main(void)
{
char type;
type = 'a';
printf("Type[%s]\n", type);
}
Now it should be obvious: when we printf("%s", x) something, printf expects x to be a string. That is, x should be a pointer to (i.e. the address of) the first element of a character array which ends with a null character. Instead we've given it a character (in this case 'a'), which it interprets as a number (in this case 97), and it tries to go to that address in memory and start reading; we're lucky to get nothing worse than a segfault. The fix is easy: decide whether type should be a char or a char[], if it's char then change the printf statement to "%c", if it's char[] then change its declaration.
Now an easy way. If we're using a good compiler like gcc, it will warn us that we're doing something fishy:
gcc foo.c -o foo
foo.c:35: warning: format ‘%s’ expects type ‘char *’, but argument 4 has type ‘int’
In future, there's a way you can save yourself all this trouble. Instead of writing a lot of code, getting a mysterious bug and backtracking, you can write in small increments. If you had added one term to that printf statement at a time, you would have seen exactly when the bug appeared, and which term was to blame.
Remember: start small and simple, add complexity a little at a time, test at every step, and never add to code that doesn't work.
I have a kernel module that is hooking the read syscall. One of the things I have to do, is capture the contents of the read syscall that is doing an external program submitted by my teacher.
With the strace, I was able to see how the program of my teacher is doing the read:
read(6, "\v\0\0\0\tExercise1", 14)
And the read hooking is working, the problem is, I don't know how to read the contents from inside the new read function because if I am correctly *buf is empty and is not filled until the original syscall read is called. So, in theory I should read directly from the file descriptor, but without using read syscall I don't know how to do that.
Any ideas? Thanks!
Basically, your hooking function should be something like the following :
size_t my_hooked_read(int fildes, void *buf, size_t nbytes)
{
size_t ret;
//Do something before original call
ret = original_read(fildes, buf, nbytes); //call the original read !
//Do something after original call
//buf is correctly filled here !
return ret;
}
If you want to read the content of buf, read it after the original call.
I am trying to send data using the serial port but the write command always returns -1.
This is the code for the write command.
int WriteComm( int Comid, void *buf, int nobtw )
{
unsigned long nobw;
nobw = write(Comid, buf, nobtw);
move(10,5);
perror("");
sleep(10);
return nobw;
}
and this is the code that calls it
gnobw = WriteComm(theApp.idComDev[Seg],&head[1],1); //send network address
I am getting invalid argument as the error but after looking on google I cant find anything explaning what this means or how to fix it. the closes thing I found was this but it uses st0 not ttyS0 so im not sure if its even the same thing.
can anyone explain what i am doing wrong to get this error and how to fix it ?
You should only be examining errno (this includes calling perror()) if the write call failed, which it indicates by returning -1. If the write succeeds, it leaves errno unchanged.
In order to test for this you should really be assigning the return value to a variable with a signed type - preferably ssize_t - not an unsigned long.
You're getting EINVAL back from write( ). That means one of your arguments to the function is invalid: EINVAL = *E*rror, *INVAL*id argument. There are three arguments to the function:
arg your variable
---------------------- -------------
int file descriptor: Comid
void *buf: buf
size_t size: nobtw
write( ) puked when it saw one of those three. So one of those three is wrong.
So put a printf( ) before the call to write( ) and see which one (or two; or three) is wrong.
Where is the actual code (not your memory of the code) that does the open( )? Is the file descriptor returned by open( ) the same one (Comid) you are trying to write( ) onto? If not, there's your problem.
That is the likely error in this mashup.
EINVAL from write(3) means:
The STREAM or multiplexer referenced by fildes is linked (directly or indirectly) downstream from a multiplexer.
What this basically means is that something else has your serial port open for writing at the same time -- at least intermittently. USB to serial converters seem to be particularly vulnerable to this. Other serial drivers will generally only allow you to open them once.
Source:
http://linux.die.net/man/3/write
according to a hint from another thread I want to analyze pointers, if their dereferencing would cause an segmentation faults or not. The idea is to write code like this:
bool IsPointerValid( void* pPointer )
{
// when opening "/tmp/hugo" here, it works fine... but not with /dev/null??
int iFD = open( "/dev/null", O_WRONLY );
int iBytesWritten = write( iFD, pPointer, 4 );
close( iFD );
return iBytesWritten > 0;
}
But whatevery I pass to IsPointerValid(..), it returns always true - because iBytesWritten is always 4. But when opening "/tmp/testfile" or a fifo, it works like expected: Passing the NULL pointer to write(..), it returns -1.
What is the reason for this special treating of "/dev/null"?
Thanks,
Charly
Because it's a special file, so it brings its own definition of read and write. (The linux kernel implements polymorphism using function pointers in the driver layer). Apparently the version of write provided by the /dev/null device doesn't use the pointer you pass in.