How to swap two open file descriptors? How to swap two open file descriptors? unix unix

How to swap two open file descriptors?


I think my best options are using dup() and/or dup2(), but according to the their man page, dup() creates a copy of the fd passed to the function, but which refers to the same open file description,

Yes.

meaning that the two can be used interchangeably,

Maybe. It depends on what you mean by "interchangeably".

and dup2() closes the new fd which replaces the old fd.

dup2() closes the target file descriptor, if it is open, before duping the source descriptor onto it. Perhaps that's what you meant, but I'm having trouble reading your description that way.

So my assumptions on what would happen are (excuse my crappy pseudo code)

int fd1, fd2, fd3;fd1 = socket(x); // what the app is aware offd2 = socket(y); // first to connectfd3 = dup(fd1); // fd1 and fd3 indentify the same description

Good so far.

dup2(fd2, fd1); // The description identified by fd2 is now identified by fd1, the description previously identified by fd1 (and fd3) is closed

No, the comment is incorrect. File descriptor fd1 is first closed, and then made to be a duplicate of fd2. The underlying open file description to which fd1 originally referred is not closed, because the process has another open file descriptor associated with it, fd3.

dup2(fd3, fd2); // The description identified by fd3 (copy of fd1, closed in the line above) is identified by fd2 (which can be closed and reassigned to fd3) since now the thescription that was being identified by fd2 is being identified by fd1.

Which looks fine, except for the fact that the first dup2() closes fd1,

Yes it does.

which closes also fd3

No it doesn't.

since they are identifying the same file description.

Irrelevant. Closing is a function on file descriptors, not, directly, on the underlying open file descriptions. In fact, it would be best not to use the word "identifying" here, for that suggests that file descriptors are some kind of identifier or alias for open file descriptions. They are not. File descriptors identify entries in a table of associations with open file descriptions, but are not themselves open file descriptions.

In short, your sequence of dup(), dup2(), and dup2() calls should effect exactly the kind of swap you want, provided that they all succeed. They do, however, leave an extra open file descriptor hanging around, which would yield a file descriptor leak under many circumstances. Therefore, don't forget to finish up with a

close(fd3);

Of course, all that assumes that it is the value of fd1 that is special to the application, not the variable containing it. File descriptors are just numbers. There is nothing inherently special about the objects that contain them, so if it is the variable fd1 that the application needs to use, regardless of its specific value, then all you need to do is perform an ordinary swap of integers:

fd3 = fd1;fd1 = fd2;fd2 = fd3;

With respect to the edit, you write,

When the application calls api_connect(), it passes to the function a pointer to an int (together with all the necessary addresses and protocols etc). api_connect() will call socket(), bind() and connect(), the important part is that it will write the return value of socket() in the memory parsed through the pointer.

Whether api_connect() returns the file descriptor value by writing it through a pointer or by conveying it as or in the function's return value is irrelevant. The point remains that it is the value that matters, not the object, if any, containing it.

This is what I mean by "The socket is only aware of one fd". The application will then call FD_SET(fd1, write_set), call a api_select() and then check if the fd is writable by calling FD_ISSET(fd1, write_set).

Well that sounds problematic in light of the rest of your description.

[Under some conditions,] api_select() creates a new connection on a different interface (calling all the necessary socket(), bind() and connect()). This connection is identified by a new fd -fd2- the application doesn't know about, and which is tracked in the API.

Now, if the application calls api_select() with FD_SET(fd1, write_set) and the API realises that is the second connection that has completed, thus making fd2 writable, I want the application to use fd2. The problem is that the application will only call FD_ISSET(fd1, write_set) and write(fd1) afterwards, that's why I need to replace fd2 with fd1.

Do note that even if you do swap file descriptors as described in the first part of this answer, that will have no effect on either FD's membership in any fd_set, for such membership is logical, not physical. You will have to manage fd_set membership manually if the caller relies on that.

It is unclear to me whether api_select() is intended to provide services for more than one (caller-specified) file descriptor at the same time, as select() can do, but I imagine that the bookkeeping required for it to do so would be monstrous. On the other hand, if in fact the function handles only one caller-provided FD at a time, then mimicking the interface of select() is ... odd.

In that case, I would strongly urge you to design a more suitable interface. Among other things, such an interface should moot the question of swapping FDs. Instead, it can directly tell the caller what FD, if any, is ready for use, either by returning it or by writing it through a pointer to a variable specified by the caller.

Also, in the event that you do switch, one way or another, to an alternative FD, do not overlook managing the old one lest you leak a file descriptor. Each process has a pretty limited quantity of those available, so a file descriptor leak can be much more troublesome than a memory leak. In the event that you do switch, then, are you sure you really need to swap, as opposed to just dup2()ing the new FD onto the old, then closing the new?