top | item 44682760

(no title)

EdSchouten | 7 months ago

If only there was a variant of execve() / posix_spawn() that simply took a literal array of which file descriptors would need to be present in the new process. So that you can say:

    int subprocess_stdin = open("/dev/null", O_RDONLY);
    int subprocess_stdout = open("some_output", O_WRONLY);
    int subprocess_stderr = STDERR_FILENO; // Let the subprocess use the same stderr as me.
    int subprocess_fds[] = {subprocess_stdin, subprocess_stdout, subprocess_stderr};
    posix_spawn_with_fds("my process", [...], subprocess_fds, 3);
Never understood why POSIX makes all of this so hard.

discuss

order

oguz-ismail|7 months ago

It's not hard, just a bit too long:

    #include <fcntl.h>
    #include <spawn.h>
    
    int
    main(void) {
      posix_spawn_file_actions_t file_actions;
      posix_spawn_file_actions_init(&file_actions);
      posix_spawn_file_actions_addopen(&file_actions, 0, "/dev/null", O_RDONLY, 0);
      posix_spawn_file_actions_addopen(&file_actions, 2, "/dev/null", O_WRONLY, 0);
      posix_spawnp(NULL, "ls", &file_actions, NULL, (const char *[]){"ls", "-l", "/proc/self/fd", NULL}, NULL);
      posix_spawn_file_actions_destroy(&file_actions);
    }

alerighi|7 months ago

It's something trivial to write (~20 lines of code), there is no point for standard library to provide that kind of functions in my opinion.

You do after the fork() (or clone, on Linux) a for loop that closes every FD except the one you want to keep. In Linux there is a close_range system call to close a range of in one call.

POSIX is an API designed to be a small layer on the operating system, and designed to make as little assumption as possible to the underlying system. This is the reason why POSIX is nowadays implemented even on low resources embedded devices and similar stuff.

At an higher level it's possible to use higher level abstractions to manipulate processed (e.g. a C++ library that does all of the above with a modern interface).

deathanatos|7 months ago

… what POSIX API gets you the open FDs? (Or even just the maximum open FD, and we'll just cause a bunch of errors closing non-existent FDs.)

o11c|7 months ago

It is always a bug to call `closerange` since you never know if a parent process has deliberately left a file descriptor open for some kind of tracing. If the parent does not want this, it must use `O_CLOEXEC`. Maybe if you clear the entire environment you'll be fine?

That said, it is trivial to write a loop that takes a set of known old and new fd numbers (including e.g. swapping) produces a set of calls to `dup2` and `fcntl` to give them the new numbers, while correctly leaving all open fds open.

Y_Y|7 months ago

> Never understood why POSIX makes all of this so hard

I honestly can't say in this particular instance but always my (unpopular?) instinct im such a situation is to asdume there is a good reason and I just haven't understood it yet. It may have become irrelevant in the meantime, but I can't know until I understand, and it's served me well to give the patriarchs the benefit of the doubt in such cases.