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

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.

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 ?

How to properly close a socket opened with fdopen?

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.

Cant pass pipe's write-end as file desctiptor

Pipe's write-end file descriptor is int but some functions such Tcl_MakeFileChannel accepts void* as file handle. I need to pass pipe write-end to it. How can one convert one type of file handle to another?
Reading this reference it seems that you simply pass the file descriptor:
Tcl_MakeFileChannel(reinterpret_cast<void*>(fd), ...);
Also you might want to read this thread.

atomic append on a file descriptor, but at what offset?

in unistd.h
using open() with the O_APPEND flag gives atomic writes always to the end of the file...
this is great and all, but what if i need to know the offset at which it atomically appended to the file...?
i realize O_APPEND is often used for log files, but I'd actually like to know at what offset in the file it atomically appended.
I don't see any obvious way to do this..? Does anyone know?
Thanks
To get the current position in a file descriptor, use lseek() with offset 0 and whence SEEK_CUR.
int fd = open(...);
if (fd) {
off_t positionWhereAppendingBegins = lseek(fd, 0, SEEK_CUR);
write(...);
close(fd);
}
Note that this will not give you reliable results if the descriptor was opened some other way, i.e. via socket().
The file is written to at the file offset as obtained by the process when the file was opened. If another process writes to the file between the open and the write, then contents of the file are indeterminate.
The correct method of handling multiple process writing to a single file is for all processes to open the file with the O_APPEND flag, obtain an exclusive lock and once the lock is obtained, seek to the end of the file before writing to the file, and finally close the file to release the lock.
If you want to keep the file open between writes, initiate the process by opening the file with the O_APPEND flag. The writing loop in this case is obtain the exclusive lock, seek to the end of the file, write to the file and release the lock.
If you really need the file position, lseek will return the file offset of the callers file descriptor at the time of the call.