4
int fcntl(int fd, int command, ... /* arg */ );

Is it portable: flags = fcntl(fd, F_GETFL); (note: no arg)?

Both Linux and FreeBSD man pages say that arg is ignored:

F_GETFL (void)
    Get the file access mode and the file status flags; arg
    is ignored.

void in Linux documentation means that arg is not required.

Here's a usage example from POSIX for a related F_GETFD flag:

#include <unistd.h>
#include <fcntl.h>
...
    int flags;


    flags = fcntl(fd, F_GETFD);
    if (flags == -1)
        /* Handle error */;
    flags |= FD_CLOEXEC;
    if (fcntl(fd, F_SETFD, flags) == -1)
        /* Handle error */;"

It shows that arg is not required for F_GETFD (today). Then it says:

The arg values to F_GETFD, F_SETFD, F_GETFL, and F_SETFL all represent flag values to allow for future growth.

Does it imply that F_GETFL might use arg in the future?

A quick search for "F_GETFL" on Ohloh code creates an impression that most open-source projects do pass arg(usually 0, sometimes NULL, or even (broken?) &fl). I don't understand why fcntl(fd, F_GETFL, 0) is the preferred form. @Wumpus Q. Wumbley suggests that it might be caused by "Advanced Programming in the UNIX Environment" book that also uses fcntl(fd, F_GETFL, 0) form.

Is there a system/compiler that requires the 3rd arg: flags = fcntl(fd, F_GETFL, 0);? Can fcntl(fd, F_GETFL) and fcntl(fd, F_GETFL, 0) produce different results today or in the future (assuming a compliant implementation)?

Community
  • 1
  • 1
jfs
  • 399,953
  • 195
  • 994
  • 1,670
  • @mafso: can I use `flags = fcntl(fd, F_GETFL);`? Or `flags = fcntl(fd, F_GETFL, 0);` should be used instead e.g., for portability? Note: the POSIX shows example for `F_GETFD` but I'm asking about `F_GETFL`. – jfs Jul 31 '14 at 14:41
  • In the FreeBSD base system, both forms are used. But in most cases the third argument 0 *is* used. But this could be for historical reasons since `fcntl` dates back to 4.2BSD. – Roland Smith Aug 04 '14 at 16:13
  • @RolandSmith: it seems as an instance of [cargo-cult programming](http://en.wikipedia.org/wiki/Cargo_cult_programming): `0` is used by APUE as pointed out by @Wumpus Q. Wumbley and therefore people might copy the code without understanding it (I see no indication that `0` is necessary on *any* system/compiler). – jfs Aug 05 '14 at 01:25
  • @J.F.Sebastian Or it could be that the developers consider it too trivial to spend the effort of removing it throughout the codebase. – Roland Smith Aug 05 '14 at 09:23
  • @RolandSmith: how do you explain the `0` appearing in the codebase in the first place? – jfs Aug 05 '14 at 09:55
  • @J.F.Sebastian Legacy of 4.4BSD, where the manual page listed the argument as [mandatory](https://svnweb.freebsd.org/base/release/2.0/lib/libc/sys/fcntl.2?revision=1573&view=markup), even though the actual header does [not](https://svnweb.freebsd.org/base/release/2.0/sys/sys/fcntl.h?revision=1541&view=markup). – Roland Smith Aug 05 '14 at 10:09
  • @RolandSmith: Could you add it as an answer? – jfs Aug 05 '14 at 10:11

3 Answers3

5

Look at the rest of the fcntl commands. Notice how some of them (F_DUPFD, F_SETFL, and others) tell you what the third arg is used for. You need to provide the third arg when using one of those. Not when using F_GETFL or F_GETFD.

In the SYNOPSIS you can see that the fcntl takes 2 args plus a ... which means the third arg can be omitted when it's not going to be used.

After doing some more research, I found that there are some old man pages (from around the time of the first APUE) in which the SYNOPSIS implies that all 3 arguments are required. Example: http://www.freebsd.org/cgi/man.cgi?query=fcntl&manpath=FreeBSD+2.2.7-RELEASE

SYNOPSIS
     #include <fcntl.h>

     int
     fcntl(int fd, int cmd, int arg);

I can't find any evidence that it was ever actually declared that way in the header, but if it was, then compilation would fail when it was called with only 2 arguments. That would be a good reason to include the extra 0 argument in your code.

If my guess is correct and this is the actual reason for the historical use of 3-arg F_GETFL then it is a useless fossil from a time when function prototypes were new and scary and OS vendors were getting them wrong.

  • POSIX says: *"The arg values to F_GETFD, F_SETFD, F_GETFL, and F_SETFL all represent flag values to allow for future growth."* i.e., it is not clear whether `fcntl(fd, F_GETFL)` is always the same as `fcntl(fd, F_GETFL, 0)` according to the docs. There could be also a system that doesn't follow the documentation. – jfs Jul 31 '14 at 15:14
  • Interesting. I didn't notice that under APPLICATION NOTES. I think it's just a badly written warning. For the SET functions, *arg* actually is the set of flags, but the GET functions return the flags. What they mean to say there is that you shouldn't blindly call `fcntl(fd, F_SETFL, O_NONBLOCK)` to turn on the flag you're interested in because you might be turning off some other flags that you don't know about. –  Jul 31 '14 at 15:22
  • And the last nail in the coffin for your theory that you might have to provide a third arg on some hypothetical POSIX-compliant system is that there is nothing in the specification that tells you what value that arg should be! You picked 0 out of pure intuition, apparently. If it was intended that 0 should always be supplied as a kidn of version check, then the number 0 would actually be mentioned, not left to guesswork. –  Jul 31 '14 at 15:39
  • See the POSIX specification of [`fcntl()`](http://pubs.opengroup.org/onlinepubs/9699919799/functions/fcntl.html). It makes no observations about argument values to the 'getter' operations being reserved. The 'setter' operations are different. Some of the 'getters' (`F_GETLK` for example) require the third argument. – Jonathan Leffler Jul 31 '14 at 15:45
  • @JonathanLeffler: [the code search](http://code.openhub.net/search?s=%22F_GETFL%22&p=3&pp=0&fl=C&mp=1&ml=1&me=1&md=1&ff=1&filterChecked=true) suggests that `fcntl(fd, F_GETFL, 0)` is common. I don't understand why. – jfs Jul 31 '14 at 16:03
  • 1
    According to that code search, one place where it's found is in APUE, which probably explains most of the other hits. Lots of people learned it from there. The question now: did APUE give a reason? I don't have the book. –  Jul 31 '14 at 16:22
  • @WumpusQ.Wumbley: I've looked [where APUE mentions F_GETFD](http://books.google.com/books?id=kCTMFpEcIOwC&printsec=frontcover&dq=Advanced+Programming+in+the+UNIX+Environment&hl=en&sa=X&ei=9HzaU4mpJsfY4QTMioGACA&ved=0CCUQ6AEwAA#v=onepage&q=F_GETFL&f=false) and hasn't found a reason: why it uses `fcntl(fd, F_GETFL, 0)` instead of `fcntl(fd, F_GETFL)`. – jfs Jul 31 '14 at 17:37
2

These two calls

flags = fcntl(fd, F_GETFL);
flags = fcntl(fd, F_GETFL, 0);

are equivalent because the third variadic parameter is ignored. If you take a look at fs/fcntl.c:262 and fs/fcntl.c:269 you will see that arg is not used.
However, when you need to set some value, you should pass the value you want to set.
You could consider fcntl as the OOP equivalent of the getters and setters.

Is there a system that requires the 3rd arg: flags = fcntl(fd, F_GETFL, 0);?

I am not aware of any. Linux doesn't, at least. The documentation clearly states that it's ignored as there would be no use of arg. The fact that you can pass something does not imply you have to.

Can fcntl(fd, F_GETFL) and fcntl(fd, F_GETFL, 0) return different result on some system (in the past, today, and in the future (assuming the conforming implementation))?

lxr allows us to dig into older versions of Linux and even in the 1991, with Linux 2.0.4, fcntl passed over the third argument. There is no meaning to accept any further argument. Therefore, if there's a system where that third argument has a meaning, it isn't Linux for sure.


I've made some further research and I've found some conflicting results. Look at here: there are some books (I don't post a direct link to them as they might be protected) which do propose fcntl as a three arguments function. But, at least regarding GETFL/GETFD, no mention is done about arg.
IMHO, its behavior has never been taken seriously: that is, what makes you wonder. Furthermore, I would even consider it harmful to use that third argument: what if it does/will have a meaning?

I've gone through Linux again, and the third argument for those commands is never passed.

Finally

Is there a system/compiler that requires the 3rd arg: flags = fcntl(fd, F_GETFL, 0);? Can fcntl(fd, F_GETFL) and fcntl(fd, F_GETFL, 0) produce different results today or in the future (assuming a compliant implementation)?

The future is mystery. Today I feel enough sure to exclude that. However, in the past it might have been so.

edmz
  • 8,220
  • 2
  • 26
  • 45
  • The source is for Linux (and it corresponds to the documentation that specifies `void` for the parameter). The question: is it portable? Can `fcntl(fd, F_GETFL)` and `fcntl(fd, F_GETFL, 0)` return different result on some system (in the past, today, and in the future (assuming the conforming implementation))? – jfs Jul 31 '14 at 15:04
2

In the FreeBSD base system, both fcntl(fd, F_GETFL) and fcntl(fd, F_GETFL, 0) are used. But in most cases the third argument 0 is used. This could be for historical reasons since fcntl dates back to 4.2BSD, and was imported into FreeBSD via the 4.4BSD-Lite source code.

In 4.4BSD (and FreeBSD 2.0), the manual page listed the argument as mandatory: int fcntl(int fd, int cmd, int arg), even though the actual old-school header does not: int fcntl(int, int, ...).

As to the why, that will be difficult to answer. We'd have to ask the original author(s). But since there are no (user)names recorded in the original source control tags I wouldn't know how to trace them.

The extra 0 argument was probably never removed in the FreeBSD codebase because it doesn't break anything and so isn't important enough to "fix".

Roland Smith
  • 42,427
  • 3
  • 64
  • 94