19

I'm implementing a library to run commands. The library is C, on Linux.

It currently does a popen() call to run a command and get output. The problem is that the command inherits all currently open file handlers.

If I did a fork/exec I could close the handlers in child explicitly. But that means re-implementing popen().

Can I set close-on-exec on all handlers without looping through them one by one?

Can I set close-on-exec as default for the process?

Thanks!

n-alexander
  • 14,663
  • 12
  • 42
  • 43
  • If you do loop through the file descriptors, read the directory /proc/self/fd/ to process only the ones that are in use rather than every possible file descriptor. – mark4o Oct 31 '09 at 22:10

2 Answers2

29

No and no.

You simply need to be careful and set close-on-exec on all file descriptors you care about.

Setting it is easy, though:

#include <fcntl.h>
fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);

#include <unistd.h>
/* please don't do this */
for (i = getdtablesize(); i --> 3;) {
    if ((flags = fcntl(i, F_GETFD)) != -1)
        fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
}

If you are running Linux kernel ≥2.6.23 and glibc ≥2.7, open (along with other similar syscalls) accepts a new flag O_CLOEXEC:

#include <unistd.h>
fd = open("...", ... | O_CLOEXEC);

If you are running Linux kernel ≥2.6.24 and glibc ≥2.7, fcntl accepts a new argument F_DUPFD_CLOEXEC:

#include <fcntl.h>
newfd = fcntl(oldfd, F_DUPFD_CLOEXEC);

If you are running Linux kernel ≥2.6.27 and glibc ≥2.9, there are new syscalls pipe2, dup3, etc., and many more syscalls gain new *_CLOEXEC flags:

#define _GNU_SOURCE
#include <unistd.h>
pipe2(pipefds, O_CLOEXEC);
dup3(oldfd, newfd, O_CLOEXEC);

Note that POSIX specifies that

The popen() function shall ensure that any streams from previous popen() calls that remain open in the parent process are closed in the new child process.

so if you're worried about that leak, don't be.

ephemient
  • 198,619
  • 38
  • 280
  • 391
  • being a library, I don't control how my users open files, so using flags in open() is not an option – n-alexander Oct 30 '09 at 15:18
  • Too bad, you're out of luck -- UNIX (and Linux) will not let you change the underlying semantics of `open` et al. You'll just have to live with it, like everybody else. – ephemient Oct 30 '09 at 19:44
  • See http://lkml.org/lkml/2004/3/29/191 for an example of a cloexec-by-default-switch proposal being shot down. This has been proposed many times and been declined just as many times. – ephemient Oct 30 '09 at 19:48
  • 1
    just backward compatibility again. Too bad I've seen lots of cases when inherited files got stuck because a loose child didn't exit, and since it's loose there isn't much you can do about it. Not even kill easily. Is manual fork/exec with close really the only way to build something reliable? – n-alexander Nov 02 '09 at 11:14
  • I don't see the problem. You can `shutdown` your sockets, and open file descriptors don't lock directory entries. Write working code and you never have to worry... and for the rest of the time it's an optimization/convenience. – Matt Joiner Mar 01 '11 at 10:33
  • "UNIX (and Linux) will not let you change the underlying semantics of open et al" hm, maybe you can't change the system, but you can always write your own wrapper functions as I do, and use them instead. Maybe name them with an uppercase letter, such as Open, Dup, etc. This can also be good if you'd like to use centralised error handling or exceptions. – Sam Watkins Mar 14 '12 at 00:02
  • Do we have the same way to pass O_CLOEXEC on "socket" calls, there is still a race condition. – Lothar Mar 22 '14 at 15:42
  • 1
    @Lothar `socket(AF_INET, SOCK_STREAM|SOCK_CLOEXEC, 0)` – ephemient Mar 24 '14 at 01:16
-2

Kinda old question, but how about use getpid() in child after fork(), then look into /proc/PID/fd/ directory and just close all descriptors you find there (except 0, 1, 2 and the one you get from opendir()) and then execve()?

A5C1D2H2I1M1N2O1R2T1
  • 190,393
  • 28
  • 405
  • 485
  • I might be missing something, but sounds like a valid workaround on Linux: https://mail.gnome.org/archives/gtk-devel-list/2015-March/msg00044.html. Have my up. – Victor Sergienko Jul 02 '18 at 18:47
  • It is a totally valid approach -- it's what is usually done in fact. I'd use `/proc/self/fd` for fds though, since it's one step less – dragonroot Nov 23 '18 at 19:53