63

I have a file descriptor stored in a variable say var. How can I check whether that descriptor is valid at a later stage?

  fdvar1= open(.....);
  fdvar2 = fdvar1;       // Please ignore the bad design

  ....
  // lots of loops , conditionals and threads. It can call close(fdvar2) also.  
  ....

  if(CheckValid(fdvar1)) // How can I do this check  ?
    write(fdvar1, ....);

Now i want to check whether var1 (which still holds the opened descriptor) is still valid. Any API's for that ?

Lunar Mushrooms
  • 8,358
  • 18
  • 66
  • 88

6 Answers6

100

fcntl(fd, F_GETFD) is the canonical cheapest way to check that fd is a valid open file descriptor. If you need to batch-check a lot, using poll with a zero timeout and the events member set to 0 and checking for POLLNVAL in revents after it returns is more efficient.

With that said, the operation "check if a given resource handle is still valid" is almost always fundamentally incorrect. After a resource handle is freed (e.g. a fd is closed), its value may be reassigned to the next such resource you allocate. If there are any remaining references that might be used, they will wrongly operate on the new resource rather than the old one. Thus, the real answer is probably: If you don't already know by the logic of your program, you have major fundamental logic errors that need to be fixed.

amccormack
  • 13,207
  • 10
  • 38
  • 61
R.. GitHub STOP HELPING ICE
  • 208,859
  • 35
  • 376
  • 711
  • 1
    @R Assuming I am trying to implement a library that handles a FD, for example the pipe library, and I am the one that passes the FD arguments to the programs that will be using my library is it still bad practice to test for FD validation? Or is it mandatory? – Ravid Goldenberg Dec 18 '14 at 08:00
  • 1
    @petric: I don't understand your question. As a general answer, I'm going to say yes, it's bad (and meaningless) to "test for FD validation" but if you have specific concerns about what you're doing you should probably post a new question asking about it (and referring to this question/answer) rather than asking in a comment. – R.. GitHub STOP HELPING ICE Dec 18 '14 at 16:32
  • What if I have parent and child process sharing a pipe. In parent process I close the read end and write end of the pipe. Then the child process still has an valid read end. How does this work? – Helping Bean Oct 28 '18 at 10:52
  • @HelpingBean: I don't see how that has anything to do with this question. If you have a real question there, please post it as a new question (and feel free to @ me here with a link to the new question). – R.. GitHub STOP HELPING ICE Oct 28 '18 at 17:58
  • That's interesting. But in case I'm unit testing a structure containg some file descriptors. And I want to test a function closing the file descriptors and reclaiming the memory. Would it be correct to check if the file descriptors are closed in such a case? – St.Antario Apr 06 '19 at 09:26
  • One reason one could want to do that: your program is called by a script which does "exec 0<&-; exec 1>&-; ./foobar". Here, you might want to check if the FDs are open or not. This kind of things can be setup in, for example, scripts starting daemons (as in daemontools, which have no reason to keep those FDs open). – user3459474 Jan 22 '21 at 02:05
  • "Thus, the real answer is probably: If you don't already know by the logic of your program, you have major fundamental logic errors that need to be fixed." I don't think this is true - when a process starts, it could have inherited any number of open file descriptors (or none). – craig65535 Oct 02 '22 at 05:43
  • @craig65535: Unless your program was told via a command line interface documented to provide a contract for feeding in extra inherited file descriptors, *it does not own any of them*. For example they could be dummy pipes the invoking process is using to monitor when your process (and its children) have all exited. It's an error to try to poke at these. – R.. GitHub STOP HELPING ICE Oct 02 '22 at 13:57
37

You can use the fcntl() function:

int fd_is_valid(int fd)
{
    return fcntl(fd, F_GETFD) != -1 || errno != EBADF;
}
  • 3
    `F_GETFD` is cheaper in principle since it only dereferences the (process-local) file descriptor in kernel space, not the underlying open file description (process-shared) which it refers to. – R.. GitHub STOP HELPING ICE Sep 09 '12 at 16:22
  • 2
    You should set `errno` to zero before calling `fcntl`. `errno` could equal `EBADF` before you call your function and `fcntl` will only change it in case of error. – Bilow Nov 29 '16 at 15:08
  • 6
    @Bilow he's already checking for a return value of `-1`, which only happens "in case of error", so setting `errno` to 0 before the call would just be redundant. – Craig Barnes Apr 24 '19 at 10:04
  • 3
    @CraigBarnes Indeed – Bilow Apr 25 '19 at 10:45
7

I don't think there is any function that can tell you if the descriptor is still valid. The descriptor is typically just a small integer like 6 and your libc can choose to reuse that number if you close the file and open a new one later.

Instead, you should consider using dup() to copy the file descriptor. By duplicating the file descriptor instead of using the same descriptor in multiple places, it might become easier for you to know whether the file descriptor is still valid. You just have to remember to close both the original descriptor and the duplicated one when you are done.

David Grayson
  • 84,103
  • 24
  • 152
  • 189
  • 4
    David is "correct" in that you can't determine if a descriptor is "*still* valid". You can tell if it's valid, but this gives you no information about whether it still refers to the same resource or not. – R.. GitHub STOP HELPING ICE Sep 09 '12 at 16:33
  • 3
    Yes. My definition of valid is that it still points to the same thing it used to. I think that's a good definition because it's usually what you want and you can tell if the descriptor is still valid by just looking at the code; it doesn't depend on undefined behavior. – David Grayson Sep 09 '12 at 16:36
  • 2
    Also, using `dup` is decent advice *for OP's particular problem*. It would avoid the whole issue. A better approach might be to keep a flag in a separate variable indicating whether the file descriptor was already closed; this will avoid extra trips to kernelspace (and scarce resource consumption). – R.. GitHub STOP HELPING ICE Sep 09 '12 at 17:09
  • Hey guys, I updated my answer (over 2 years later) to be clearer. I am not suggesting that you call `dup` just to see if the file descriptor is valid; I was trying to offer some design advice that might make it easier to avoid that situation in the first place. – David Grayson Mar 15 '15 at 17:32
7

From this forum article:

int is_valid_fd(int fd)
{
    return fcntl(fd, F_GETFL) != -1 || errno != EBADF;
}

fcntl(GETFL) is probably the cheapest and least likely to fail operation you can perform on a file descriptor. In particular, the specification suggests that it cannot be interrupted by signals, nor is it affected by any sort of lock held anywhere.

Kev
  • 118,037
  • 53
  • 300
  • 385
wbao
  • 205
  • 1
  • 5
  • 3
    How about attributing your sources: http://cboard.cprogramming.com/networking-device-communication/93683-checking-if-file-descriptor-valid.html#post678998 – Kev Sep 09 '12 at 22:28
1

It seems to me that if you want to know if it still points to the same resource, one (non-perfect) approach would be to fstat() the descriptor just after open and then later you can do it again and compare the results. Start by looking at .st_mode& S_IFMT and go from there -- is it a filesystem object? Look at .st_dev / .st_ino. Is it a socket? Try getsockname(), getpeername(). It won't be 100% certain, but it can tell you if it definitely isn't the same.

Vikalp Patel
  • 10,669
  • 6
  • 61
  • 96
John Hascall
  • 9,176
  • 6
  • 48
  • 72
-1

i solved this problem for me. i don't know whether it can be used for general purpose but for serial connections it works fine (e.g. /dev/ttyUSB0)!

struct stat s;
fstat(m_fileDescriptor, &s);

// struct stat::nlink_t   st_nlink;   ... number of hard links 
if( s.st_nlink < 1 ){
 // treat device disconnected case
}

For details see e.g. man page http://linux.die.net/man/2/fstat

Cheers, Flo

flozn
  • 199
  • 1
  • 3