File descriptors across exec

cexecfile-descriptors

By default file descriptors remain open across the exec functions. The benefit is perhaps understandable for descriptors 0-2. But is there a practical use case for keeping other descriptors open? Are there any real applications that rely on this fact?

Best Answer

There's a flag you can set on a file descriptor (upon open(): O_CLOEXEC or later with fcntl(): FD_CLOEXEC) if you don't want that fd to be passed to executed commands.

That's what you should do for your internal file descriptors if you're going to execute commands.

In shells, that's what ksh93 does when you do exec 3< some-file for instance. For other shells or fds opened with { cmd1; cmd2; } 3< file, you'd need to close by hand if you don't want cmd1 or cmd2 to access that fd: {cmd1 3<&-; cmd2; } 3< file. That's good practice but not always followed as it's usually not critical if you don't do it.

Now, whether the feature is useful. Yes, several commands rely on it.

A few commands take a file descriptor as argument that is meant to have been opened by a caller. A few examples that come to mind:

  • xterm with its -S option
  • qemu for various things
  • flock (to lock a file on the caller's fd)
  • the test command aka [ for it's -t option (OK the test utility is built in most Bourne-like shells nowadays, but there still is a test command that can be executed).
  • dialog needs file descriptors for input from the user, output and error to the user and input and output to the caller, so you can use extra fds for that.
  • gpg or openssl to which you can specify a file descriptor to communicate the passphrase or other information.

There are a number of helper utilities (for instance, the need to execute could be to run a part of a command as a different user or group using a setuid/setgid executable) that rely on that.

Process substitution relies on it:

In, diff <(cmd1) <(cmd2), 2 file descriptors (to pipes) are passed to diff and diff accesses them by opening them via the special /dev/fd/n passed as argument.

For shells that don't have process substitution, you do the same by hand with things like:

cm1 | { cmd2 | diff /dev/fd/3 -; } 3<&0
Related Question