3 ms·
> The same thing could happen with fd3: Your shell spawns programA, which knows nothing about fd3. programA then spawns children programB and programC in parall
by hnlmorg 4y ago
> The same thing could happen with fd3: Your shell spawns programA, which knows nothing about fd3. programA then spawns children programB and programC in parallel, both of which do. If programA does nothing to stop programB and programC from inheriting fd3, they both will, and then you are in the same situation as with a Unix domain socket path in an environment variable.
This isn’t something I’ve been able to replicate in any of my tests but I’ll take your point into consideration and take another look in case I’ve missed something.
> Actually, here's a way in which fd3 is worse than a domain socket path in an environment variable: with a Unix domain socket, you'll get two independent connections (on different FDs) from programB and programC, so even if their output gets mixed up, at least their control messages won't be. Whereas, they'll share the same fd3, and any message you receive, you won't be able to tell which one it came from–there is even some risk you'll get part of a message from one and then part of a message from another (although whether that happens or not depends on the details of buffering).
Good point.
Do you think it would be practical to reuse the same socket for everything then and use the incoming PID as a filter rather than creating a new file per invocation?
> Linux supports SOCK_SEQPACKET Unix domain sockets, in which the kernel is aware of explicit message boundaries. Unfortunately, most other Unix-like systems don't (most significantly, macOS).
Yeah. This is a cross platform project which has unfortunately placed a few restrictions on its design (though thankfully not many).
Thanks for the feedback by the way. It’s constructive comments like these which help improve indie projects :)
- skissane 4y ago> Do you think it would be practical to reuse the same socket for everything then and use the incoming PID as a filter rather than creating a new file per invocation? I was thinking you could have two environment variables: SHELL_SOCKET=<path to the Unix domain socket the shell is listening on> SHELL_CLIENT_ID=<some kind of unique ID, like a UUID> Now, SHELL_SOCKET is the same for every subprocess. But every new process you fork, you set a different value for SHELL_CLIENT_ID. And part of the protocol, is that upon connecting, the client has to send you SHELL_CLIENT_ID, and you check it is a valid one in your memory, and you reject the connection if it isn't. Since you control the protocol, you could also insist the client sends you the PID, and match that too. I wouldn't even bother trying to stop clients lying about their own PID – if someone wants to play those kind of silly games, why stop them? If the socket file ownership/permissions make it accessible by the user only, then either the user themselves is being silly, or else their account is already compromised by an attacker/malware. So then you don't even need to bother with SO_PEERCRED/SCM_CREDENTIALS/etc if you don't want to. And, with respect to the "multiple subprocesses" problem–the simplest thing you could do, is decide to only accept one connection per a SHELL_CLIENT_ID, the first one wins, the second gets rejected. There's many more complex solutions (like have some kind of locking so subprocesses could take turns at output but doing it in a way which wouldn't corrupt the output), but that would be the simplest. You could even encode both into a single environment variable, e.g: SHELL_SOCKET=<client UUID>:<path to shell socket>