When should we use fdopen and how do we use it? My understanding of that is when we can't use fopen to read (read from pipe). I don't really understand the description of fdopen on the man page: The fdopen() function associates a stream with the existing file descriptor, fd.
You use fdopen() when you have a file descriptor (int fd;) of some sort but you need to call functions that require a file stream (FILE *fp;) instead. This could be a pipe file descriptor, or a socket file descriptor, or any other file descriptor type.
Once you've used fdopen(), you should not use the file descriptor again — you should use only the file stream. If you must use a file descriptor as well, it would be best to use fileno(fp) rather than the saved fd. Most importantly, if you mix access, you need to ensure that you've flushed the file stream before you do anything with the file descriptor. (There's no buffering with the file descriptor, so there's less of a problem reverting to the file stream from the file descriptor.). Operations that change the current file position on the file descriptor could mess up the file stream and vice versa (when there is a current position associated with the file descriptor or file stream). Note that both read and write operations change the current file position — there's not a lot you can do without risking a mess.
You must use fclose(fp); to close the file stream (and implicitly the file descriptor). Do NOT use just close(fd) or close(fileno(fp)).
Note that POSIX defines dprintf() to do formatted output to a file descriptor. Also, you could use snprintf() or its relatives to format data into a string and then write the string to the file descriptor. This might make it less important to use fdopen().
Related
Under Linux I use this code to redirect stdout and stderr on a file, as shown in the code the file is opened using fopen(f) and is it closed using close(fd).
int fd;
FILE *f;
f = fopen("test.txt", "rb+");
fd = fileno(f);
dup2(fd,STDOUT_FILENO);
dup2(fd,STDERR_FILENO);
close(fd);
My question is whether the close(fd) statement closes all file descriptors, or is it necessary to use fclose(f) as well ?
The rule is to close the outermost level. Here the FILE object pointed to by f contains the fd file handle but also internal data like a possible buffer and various pointers to it.
When you use close(fd), you free the kernel structures related to the file, but all the data structures provided by the standard library are not released. On the other hand, fclose(f) will internally close the fd file handle, but it will also release all the resources that were allocated by fopen.
TL/DR: if you use fd= open(...); to open a file, you should use close(fd); to close it, but if you use f = fopen(...);, then you should use fclose(f);.
As already pointed out in the other answers, you should use fclose(f); instead of close(fd);.
My question is whether the close(fd) statement closes all file descriptors [...]
No, it won't close all file descriptors. The file descriptors STDOUT_FILENO and STDERR_FILENO will still remain open and will now refer to the opened file test.txt. However, these file descriptors should probably not be closed, as it is good programming practice for STDOUT_FILENO and STDERR_FILENO to remain valid until the end of the program. They will be automatically closed by the kernel on process termination.
C FILE* streams use buffered I/O internally. fclose() flushes this buffer and then closes the file descriptor at OS level. close()'ing a FILE* stream may not flush this internal buffer and you may lose data. So for C streams always use C fxxx() functions.
Keep in mind that when using fprintf() I'm aware that I need to pass the file descriptor for writing to the pipe. I just have the doubt and wish to know right now; I don't really have any kind of sample code.
In addition, I want to know if functions such as fputc(), fputs() will also work.
Using fprintf() requires a file stream (FILE *). When you create a pipe, you get two file descriptors. File descriptors are not the same as streams.
You can use the POSIX fdopen() function to create a file stream from a file descriptor; you can then use fprintf() on that file stream — or any of the other standard I/O functions that take an explicit file stream argument and write to the stream. You can't use fseek(); you'll get an error (ESPIPE — illegal seek).
You can (in theory) use the POSIX dprintf() function to write directly to a file descriptor — if your system supports it.
You can use fdopen() to convert a file descriptor to a FILE *, and then use all the stdio functions.
int pipefd[2];
pipe(pipefd);
FILE *pipeout = fdopen(pipefd[1], "w");
fprintf(pipeout, "Message written to the pipe\n");
I'm reading the POSIX specification and I can't fully understand how file descriptors, file descriptions and streams interact.
FILE* f1 = fopen("a.txt", "r");
int fno = fileno(f1);
FILE* f2 = fdopen(fno, "r");
// is it true?
assert(fileno(f2) == fno);
// does it close only f1 or f2 too?
fclose(f1);
fgetc(f2); // valid?
(Question is in the comments.)
The C standard library gives you the opaque pointer FILE*, a file handle, which you can manipulate with fopen()/fclose(), and access with fread()/fwrite().
POSIX offers a notion of file descriptors which are integers. You can manipulate those with open()/close(), and access with read()/write().
For every open file handle FILE * fp on a POSIX system, you can get the underlying file descriptor with fileno(fp). Conversely, for an existing file descriptor n, you can open a standard file handle with fdopen(n).
In other words, the POSIX file descriptors are an operating system primitive which is used to implement the C standard io library. Note that POSIX file descriptors also serve as handles for sockets.
Your final call to fgets() is undefined because the fclose() invalidates the file handle, and consequently its underlying file descriptor. fdopen() does not duplicate the file descriptor.
Yes, it's true and it closes both files as they use the same file descriptor.
I got a descriptor for a TCP socket in the following manner :
int desc = accept(socket_descriptor, &client_address, &len)
Now from this descriptor desc I want to get a file pointer. Can fdopen() be used here ?
The reason I want to get a file pointer is because I am making changes to an existing code that writes data to a local file. Now, I want to extend its functionality so that it can alternatively write to a TCP client. I dont want to rewrite all functions and was thinking of somehow being able to use the existing infrastructure. The existing functions use the file pointer to write to the file. I was wondering if it was possible to make the same function write to a TCP stream without making any changes.
Yes, fdopen() is exactly what you need. Here is what man page is saying about it:
The fdopen() function associates a stream with the existing file
descriptor, fd. The mode of the stream (one of the values "r", "r+",
"w", "w+", "a", "a+") must be compatible with the mode of the file
descriptor. The file position indicator of the new stream is set to
that belonging to fd, and the error and end-of-file indicators are
cleared. Modes "w" or "w+" do not cause truncation of the file. The
file descriptor is not dup'ed, and will be closed when the stream
created by fdopen() is closed. The result of applying fdopen() to a
shared memory object is undefined.
But use it with caution when applying to socket descriptors. High-level I/O functions use buffering, and may send data differently (i.e. flush whenever \n is found in the stream, insert \r) etc.
By using fdopen(), fileno() it's possible to open streams with existing file descriptors. However the proper way to close a file, once you've opened it with a stream is to fclose() the FILE pointer. How can one close the stream, but retain the open file descriptor?
This behaviour is akin to calling fflush() and then fileno(), and then never using the FILE pointer again, except in closing. An additional concern is that if you then fdopen() again, there are now multiple FILE pointers, and you can only close one of them.
If you're on a POSIXy system (which I assume you are, since you have fileno()), you can use dup() to clone the file descriptor:
int newfd = dup(fileno(stream));
fclose(stream);
Or you can hand fdopen() a duplicate file descriptor:
FILE *stream = fdopen(dup(fd), "r");
Either way, the other copy of the fd won't close with the FILE *. However, keep in mind the location pointer is shared, so be careful if you are using both at the same time. Also, any fcntl() locks held on the original fd will be released when you close the copy.
If everything else fails, dup(2) could help.