2

If I have a file descriptor that has been opened with open (2) and subsequently set to O_NONBLOCK using fcntl (2) using a FFI (Foreign Function Interface).

This means almost every call to read (2) will return -1, with errno set to EAGAIN, which means this time there was no data available.

Sadly, the FFI I am using cannot access the errno variable, so I cannot determine whether the -1 value was returned because there was no data or because the file handle is no longer valid.

So I am trying to determine somehow if the file descriptor is still valid without ever having to read errno. I already tried all the answers with fcntl from this question, but they don't work and never return -1.

Perhaps this is because I am reading a device file: /dev/input/js0?

Is there another function I can call that will tell me whether the file descriptor is invalid? (In the question above someone mentioned poll, but I am not sure how this was meant.)

I am using the Squeak FFI and am not allowed to add any custom C wrappers. I am trying to access a Gamepad and read button information from it, which is an optional assignment.

Smalltalk Source Code of what I tried with fcntl:

The FFI for fcntl (defined in Class Gamepad):

manipulateFileHandle: fileHandle command: command
    < cdecl: long 'fcntl' ( long long ) module: 'libc.so.6' >
    ^ self externalCallFailed

Then in another method, I call it:

| handleTest |
handleTest := Gamepad manipulateFileHandle: externalFileHandle command: 1.
Transcript show: handleTest; cr.

The command 1 is F_GETFD, reading the file descriptor flags. But handleTest is never -1, even after unplugging the gamepad.

Community
  • 1
  • 1
Nikon the Third
  • 2,771
  • 24
  • 35
  • Make a C function that reads `errno` for you. Or throw away that FFI, it's garbage (I know you can't). Otherwise, make a custom `read()` wrapper that returns `errno` in a useful way. – fuz May 05 '16 at 23:43
  • Oh, how I would love to throw that FFI away. It's homework, and I am not allowed to add any extra wrappers... which is also ridiculous. The code will be automatically checked out and tested, if there was a dependency on a custom wrapper, it would fail. – Nikon the Third May 05 '16 at 23:44
  • 1
    You are not allowed to write any C code? Please specify such restrictions in the question. It sucks having to discover all the untold restrictions by repeatedly interrogating you. What system are you working on? Are you allowed to write unportable code? Because most libc implementations have a function that returns a pointer to `errno`. – fuz May 05 '16 at 23:50
  • 1
    Can you provide an illustrative program of what you tried so we can see if there is some bug you introduced that caused it to not work? – jxh May 05 '16 at 23:50
  • 1
    Your `fcntl` idea should have worked. After the FD is closed, it doesn't matter what kind of device it was originally open on. – Barmar May 05 '16 at 23:51
  • @FUZxxl, yes I can write unportable code! It has to work on Ubuntu 14.04 32bit only. And sorry for not being specific enough, I added that part to the question. – Nikon the Third May 05 '16 at 23:53
  • @jxh, I added some source code, but since this is Squeak, it is impossible to paste a good overview because that would mean hundreds of clicks (god I hate that thing). – Nikon the Third May 06 '16 at 00:00
  • @Barmar, thank you. I thought so as well, but the call just never returns a negative value. – Nikon the Third May 06 '16 at 00:07
  • 3
    Can you dereference an `int *` in your smalltalk code? The function `__errno_location()` will return the address of the `errno` variable. – jxh May 06 '16 at 00:11
  • @jxh, I think so. It should be able to handle pointers using the `ExternalAddress` class. That might actually work, I will try that right away! Thank you – Nikon the Third May 06 '16 at 00:13
  • 2
    @NikontheThird Unplugging the device doesn't make the FD invalid. An FD doesn't become invalid until you call `close()` on it. – Barmar May 06 '16 at 00:13
  • @Barmar, I did not know that. That explains everything. I just assumed that if the file does not exist anymore the descriptor would be invalid as well. – Nikon the Third May 06 '16 at 00:14
  • @NikontheThird That's Windows semantics. In POSIX semantics (what Linux does), a file is only ever actually removed when the last file descriptor to that file is closed. – fuz May 06 '16 at 10:55

1 Answers1

3

On GNU/Linux systems, libc contains the function __errno_location(), which returns the address of the errno variable. Your FFI should be able to invoke that function, and then dereference the address representing an int *.

Before GCC invented a syntax for thread local variables, the technique to allow multi-threaded code to safely manipulate errno was to define it as:

#define errno (*__errno_location ())

The function would return a thread local address. On your Linux system, if you use gcc -E on C code that uses errno, you will see the expansion.

As explained by Barmar, the fcntl() techniques work. The reason it never returned -1 was because you were still holding a valid file descriptor. A file descriptor does not become invalid until it is closed.

jxh
  • 69,070
  • 8
  • 110
  • 193
  • Thank you very much. The `__errno_location` call works, now I have access to `errno`. To anyone with the same issue: You have to define a separate class that derives from `ExternalStructure` in Squeak, otherwise you'll end up with an opaque `ExternalData` pointer. The fields: `fields ^#((errno 'long'))`, the FFI call: `< cdecl: StructureName* '__errno_location' () module: 'libc.so.6' >` – Nikon the Third May 06 '16 at 00:38