How to properly close a socket opened with fdopen? - c++

I have a socket sock:
int sock = socket(...);
connect(sock, ...);
// or sock = accept(sock_listen, 0, 0);
And I opened it with fdopen twice, so that I can use the buffered reader and writer in stdio, such as fwrite, fread, fgets and fprintf.
FILE *f_recv = fdopen(sock, "wb");
FILE *f_send = fdopen(sock, "rb");
// some IO here.
close(sock);
fclose(f_recv);
fclose(f_send);
But as we know, if I fclose a file, a close will be called subsequently, and fclose will fail.
And if I use only close, the memory of struct FILE is leaked.
How do I close it properly?
UPDATE:
Use fdopen once with "r+" makes reading and writing share the same lock, but I except the sending and receiving to work individually.

Use dup() to obtain a duplicate file descriptor for passing to fdopen(). When you call fclose() that will be closed but the underlying socket will remain open and can be closed with close():
FILE *f_recv = fdopen(dup(sock), "wb");
FILE *f_send = fdopen(dup(sock), "rb");
// some IO here.
fclose(f_recv);
fclose(f_send);
close(sock);
Edit: You can of course combine this with just using a single FILE object for both reading and writing.

I think calling fdopen() twice is a mistake for the reasons you give.
Just open it once with fdopen(), passing the mode string "r+b" to make it read/write and binary.

Related

Correct way of using fdopen

I mean to associate a file descriptor with a file pointer and use that for writing.
I put together program io.cc below:
int main() {
ssize_t nbytes;
const int fd = 3;
char c[100] = "Testing\n";
nbytes = write(fd, (void *) c, strlen(c)); // Line #1
FILE * fp = fdopen(fd, "a");
fprintf(fp, "Writing to file descriptor %d\n", fd);
cout << "Testing alternate writing to stdout and to another fd" << endl;
fprintf(fp, "Writing again to file descriptor %d\n", fd);
close(fd); // Line #2
return 0;
}
I can alternately comment lines 1 and/or 2, compile/run
./io 3> io_redirect.txt
and check the contents of io_redirect.txt.
Whenever line 1 is not commented, it produces in io_redirect.txt the expected line Testing\n.
If line 2 is commented, I get the expected lines
Writing to file descriptor 3
Writing again to file descriptor 3
in io_redirect.txt.
But if it is not commented, those lines do not show up in io_redirect.txt.
Why is that?
What is the correct way of using fdopen?
NOTE.
This seems to be the right approach for a (partial) answer to Smart-write to arbitrary file descriptor from C/C++
I say "partial" since I would be able to use C-style fprintf.
I still would like to also use C++-style stream<<.
EDIT:
I was forgetting about fclose(fp).
That "closes" part of the question.
Why is that?
The opened stream ("stream" is an opened FILE*) is block buffered, so nothing gets written to the destination before the file is flushed. Exiting from an application closes all open streams, which flushes the stream.
Because you close the underlying file descriptor before flushing the stream, the behavior of your program is undefined. I would really recommend you to read posix 2.5.1 Interaction of File Descriptors and Standard I/O Streams (which is written in a horrible language, nonetheless), from which:
... if two or more handles are used, and any one of them is a stream, the application shall ensure that their actions are coordinated as described below. If this is not done, the result is undefined.
...
For the first handle, the first applicable condition below applies. ...
...
If it is a stream which is open for writing or appending (but not also open for reading), the application shall either perform an fflush(), or the stream shall be closed.
A "handle" is a file descriptor or a stream. An "active handle" is the last handle that you did something with.
The fp stream is the active handle that is open for appending to file descriptor 3. Because fp is an active handle and is not flushed and you switch the active handle to fd with close(fd), the behavior of your program is undefined.
What is my guess and most probably happens is that your C standard library implementation calls fflush(fp) after main returns, because fd is closed, some internal write(3, ...) call returns an error and nothing is written to the output.
What is the correct way of using fdopen?
The usage you presented is the correct way of using fdopen.

_close file descriptor returned from _open_osfhandle over a HANDLE

HANDLE h = CreateFile( ... );
//...
int fd = _open_osfhandle( reinterpret_cast<intptr_t>(h), 0 );
_commit( fd );
// _close(); // Not required
According to MSDN
The _close function closes the file associated with fd. The file
descriptor and the underlying OS file handle are closed. Thus, it is
not necessary to call CloseHandle if the file was originally opened
using the Win32 function CreateFile and converted to a file descriptor
using _open_osfhandle.
So according to the documentation, if I call _close() the underlying file HANDLE (my file) will close too, which is something that I don't want.
But I don't think this answers how I handle the file descriptor returned by _open_osfhandle and what happens to it. Is it ok if I just ignore the file descriptor and wait until the file is closed later ? Will file clean up (CloseHandle()) destroy the file descriptor too ?

End of file on pipe magic during open

I have a c++ application in which I am starting another process(wireshark) something like following.
if (fp == NULL){
fp = popen(processpath, "r"); //processpath is the process I want to start
if (!fp){
throw std::invalid_argument("Cannot start process");
}
fprintf(fp, d_msg);//d_msg is the input I want to provide to process
} else if(fp != NULL){
fprintf(fp, d_msg);
}
The problem is when I execute my c++ application, it does start the wireshark but with error End of File on pipe magic during open
what should I do to avoid that?
Also I tried using mkfifo to create a named pipe and execute it. I used something like this:
if (fp == NULL){
system("mkfifo /tmp/mine.pcap");
fp = popen("wireshark -k -i /tmp/mine.pcap", "r");
if (!fp){
dout << "Cannot start wireshark"<<std::endl;
throw std::invalid_argument("Cannot start wireshark");
}
input = fopen("/tmp/mine.pcap", "wb");
fprintf(input , d_msg);
fclose(input);
} else if(fp != NULL){
input = fopen("/tmp/mine.pcap", "wb");
fprintf(input , d_msg);
fclose(input);
}
But that too didn't work. With this I get following error:
The file "/tmp/wireshark_mine.pcap_20130730012654_ndbFzk" is a capture for a network type that Wireshark doesn't support
Any help would be appreciated.
Thank you very much.
The problem is when I execute my c++ application, it does start the wireshark but with error End of File on pipe magic during open
what should I do to avoid that?
You should write a pcap file or a pcap-ng file to the pipe, rather than fprintfing something.
Both of those file formats are binary. If you're constructing your own packets, you will have to construct and write to the pipe a valid pcap file header or several valid pcap-ng blocks (Section Header Block and at least one Interface Description Block) before you can write any packets, and then, for each packet, you will have to write a per-packet pcap header or the beginning and end of a pcap-ng Enhanced Packet Block before (and, for an Enhanced Block, after) the raw packet data. If you're just sending an existing file to Wireshark, you will need to read raw bytes from the file and send those raw bytes down the pipe.

How close pipe handle in unix? (fclose() of pclose())?

If I create pipe in unix this way:
int fds[] = {0, 0};
pipe(fds);
Then make FILE * from fds[0] this way:
FILE *pipe_read = fdopen(fds[0], "rt");
Then how should I close this file (pipe_read)?
fclose(pipe_read)
pclose(pipe_read)
close(fileno(pipe_read))
fdopen returns a FILE* so you should fclose it. This will also close the underlying file descriptor as well.
The pclose call is meant for closing a handle created with popen, a function you use for running a command and connecting to it with pipes.
The close call will close the underlying file descriptor but, unfortunately, before the file handle has had a chance to flush its data out - in other words, you're likely to lose data.
You should use fclose(pipe_read).
close() closes the file descriptor in the kernel. It's not enough because the file pointer is not free. So you should use fclose() on pipe_read, which will also take care of closing the file descriptor.

how to tell if a stream is closed in C before calling fclose()

I have a failing C program, and i've narrowed it down to a fork()ed child trying to close stdout and stderr, which were closed by its parent process before calling fork() - i assume those streams were passed on to the child process.
how can i tell if a stream is closed in C before attempting to close it using something like fclose(stdout)
C programs on UNIX expect to have a file descriptors 0, 1 and 2 open when they are started. If you do not want them to go anywhere, open /dev/null and dup it to those file descriptors.
If you're working at that level, you probably shouldn't be using the C standard library's buffered FILE pointers (stdin & co), but rather with the underlying file descriptor integers themselves.
You should be able to do some harmless operation, such as maybe a lseek(fd, 0, SEEK_SET) on the file to detect if the underlying descriptor is valid.
You can use ftell() to check that.
If it returns -1, your stream is mostly closed.
After the call to fclose(), any use of
stream results in undefined behavior.
So if it's the FILE* stdout, you can't use stdout anymore at all, not even to check if it's valid/open.
You could use the file descriptor for stdout directly, it's fd 1 .
struct stat stbuf;
if(fstat(1,&stbuf) == -1) {
if(errno == EBADF) {
stdout isn't open/valid
}
}