Why is os.fchmod not working with a socket file descriptor?

Question:

I’m trying to use os.fchmod() as specified in this tutorial. However, when I pass in a file descriptor and a mode, I get back a vague "Invalid Argument" error.

import socket
import stat
import os

s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
os.fchmod(s.fileno(), stat.S_IRWXO)

How do I fix this? The goal is to create a Unix Domain Socket with changed permissions (this git issue implies its possible).

Asked By: Cat

||

Answers:

In the fchmod man page on MacOS, one of the causes of EINVAL is listed as:

[EINVAL] fildes refers to a socket, not to a file.

Apparently linux (but not MacOS) supports “pre-setting” the mode so that when you eventually bind, it will get the mode you pre-set with fchmod. That is not guaranteed to work across OSes and clearly does not on MacOS (I would guess it also doesn’t work on other Unix-like OSes).

I think you will need use the umask strategy mentioned in that git issue. You can then call os.chmod with the socket’s path name after the bind to ensure that the mode is set exactly as you want it (umask only allows you to specify bits to be cleared from the mode).

As mentioned in the issue, the problem with umask is that there is only a single mask value, which is shared by all threads. If you have only a single thread, or all threads can use the same mask value for all files created, that isn’t a problem. Otherwise, you would either need to implement some kind of cooperative locking around umask usage, or use the fork hack mentioned in the issue to do the umask/bind in another process.

Answered By: Gil Hamilton