I've opened a FILE *f with fdopen(fd, "w+") and I would like to keep the fd open after closing with fclose(f).
Is there an elegant way to do that?
Can I simply call fflush(f); free(f); or is that dangerous?
Or is there a way to change the internal fd to an invalid value -1 so that fd cannot be closed by fclose()?
If you want to get the file descriptor and disassociate it from the the FILE*, there is one supported way:
Get the file descriptor with fileno(). (POSIX)
Duplicate it using dup(). (POSIX)
Close the FILE the normal way with fclose(). (c)
Now you only have a file descriptor.
If you'd really like to use a FILE object rather than a file descriptor, you needn't worry about close()ing the file descriptor: fclose() does this for you. Also, if performance is important, you should worry about all of the fdopen() and wrapped system calls defined in f*() standard I/O functions. Big thanks to all the comments!
Also see the fclose man page:
The fclose() function flushes the stream pointed to by fp (writing any buffered output data using fflush(3)) and closes the underlying file descriptor.
Related
I have a save file containing a stream of program events. The program may read the file and execute the events to restore a previous state (say between program invocations). After that any new events are appended to this file.
I could open the file once as read-write (fopen rw), not exposing the usage pattern.
But I wonder if there are any benefits of opening it as read-only at first (fopen r) and later re-opening it as append (freopen a). Would there be any appearent difference?
In your case there may not be any specific benefits, but primary use of freopen is to change the file associated with standard text stream (stdin, stdout, stderr). It may effect the readability of your code if you use if on normal files. In your case you first open in read-only mode, but if you are opening the stream as output there are few things about freopen that we need to keep in mind.
On Linux, freopen may also fail and set errno to EBUSY when the kernel structure for the old file descriptor was not initialized completely before freopen was called
freopen should not be used on output streams because it ignores errors while closing the old file descriptor.
Read about freopen and possible error conditions with fclose in GNU manual: https://www.gnu.org/software/libc/manual/html_node/Opening-Streams.html#Opening-Streams
No there are no specific benefits of opening the file as Read Only and then reopening in Append mode. If you require changes in files during program execution than better if you open it in as per mode.
I open a file with open(), I checked my file descriptor and seems to be ok (3).
But when I try to write on this fd, write() returns -1.
I also print my string, which is shown correctly.
errno = 9, "Bad file descriptor"
I checked my file descriptor and seems to be ok (3)
Your file descriptor is apparently not open for writing. From the documentation for write:
EBADF
fd is not a valid file descriptor or is not open for writing.
I would use the functions given in the standard library stdio.h. Use fopen() to open the file, fprintf() to write to a file, and fclose() to close the file. Example:
FILE *fh;
fh = fopen("test.txt","w");
fprintf(fh, "Hello World!\n");
fclose(fh);
I would bookmark this site as it's an invaluable resource for standard C programming (hint: please check there first ;)
http://www.acm.uiuc.edu/webmonkeys/book/c_guide/
I have file descriptor and inside my signal handler, i close the file. But due to other conditions, the file could have been closed earlier. Is there a way to check if the file descriptor points to an open file in c and linux?
UPDATE:
Is it possible to determine filename associated with a file descriptor? This way if the fd gets recycled, app can detect it.
Try to do any file operation like lseek on the file descriptor. If it returns -1. Then check errno, if its EBADF then the file descriptor is already closed.
Try to do lseek in an unaffected manner like below.
lseek(fd, 0, SEEK_CUR);
Usually while opening the first file in a program we always get the file descriptor as 3. After this file is closed, if we try to open some other file we will get the same file descriptor as 3. Because always we will get the lowest available number. If we are closing and reopening many files in a program, then we need to improve our code to track of file descriptors list to check whether its closed or not.
When you open a file, it always get the minimal available fd assigned. So if you close your fd, and then open another file somewhere in your code, you could easily have the same fd reassigned to this new file. So there is no reliable way to tell that the file descriptor is closed, because it can now point to another opened file.
After you closed the file descriptor fd assign -1 to it, so you later could test fd against -1 to see if you already closed it.
You could lookup the filename a (valid) file descriptor referrs to by calling readlink() on /proc/<pid>/fd/<file descriptor>.
After opening a file for writing:
FILE *file = fopen("./file", "w");
Can I assume the file was created immediately? Is it safe to call:
stat( "./file", info);
Or should I better:
fflush(file);
or
fclose(file);
beforehand?
Edit: Assume non NULL file after fopen call
The fopen manual page says:
If mode is w, wb, a, ab, w+, wb+, w+b, a+, ab+, or a+b, and the file
did not previously exist, upon successful completion, the fopen()
function shall mark for update the st_atime, st_ctime, and st_mtime
fields of the file and the st_ctime and st_mtime fields of the parent
directory.
So I think it is safe to stat on the file just after a successful fopen call.
Yes, logically we could do it. As opening a file for writing in read-only filesystem fails. This indicates that fopen()/open() does required checks. Other way to confirm is this by opening file with x similar to O_EXCL flag of open().
If the call to fopen is successful then it means that the file is created. The file may not be committed ( flushed ) to the disk though. But you need not worry about this as the next call to stat will fetch the file from the kernel buffer.
So an fflush or an fclose is not required in this particular case.
The few times where in you need to scratch your head about flushing to disk is when there is a probability of a system crash. In this case if you haven't committed data completely to the disk using something like fsync, then there might be probable data loss upon next system restart.
Does every process have stdin, stdout and stderr associated to it to the Keyboard and Terminal?
I have a small program. I want to replace the keyboard input to a file called new.txt. How do I go about it?
FILE *file1
fopen("new.txt", "r")
close(0); // close the stdio
dup2(file1, 0);
Would this work? Now my stdio is redirected to the FILE?
No, not every process. But on operating systems that give you a command-line window to type in, a program started from that command line will have stdin connected to the keyboard, and stdout and stderr both going to the terminal.
If one program starts another, then often the second program's standard streams are connected to the first program in some way; for example, the first program may have an open descriptor through which it can send text and pretend that it's the "keyboard" for the second process. The details vary by operating system, of course.
In response to your question:
Would this work ?
No. dup2() takes two file descriptors (ints) while you're passing it a FILE * and an int. You can't mix file handles (FILE *s) and file descriptors (ints) like that.
You could use open instead of fopen to open your file as a file descriptor instead of a file handle, or you could use fileno to get the file descriptor from a file handle. Or you could use freopen to reopen the stdin file handle to a new file.
Note that file descriptors (ints) are part of POSIX operating systems and are only portable to other POSIX systems, while file handles (FILE *s) are part of the C standard and are portable everywhere. If you use file descriptors, you'll have to rewrite your code to make it work on Windows.