Socket.Unix

Programs on the same machine can connect to each other through a name in the file system instead of a network address, and this module makes such connections and listeners.

A Unix domain socket is a stream socket whose address is a path. A listener creates a socket file at the path, and a client connects by naming that path. The Connection and Listener made here are the same types as for TCP, and are used through Socket; what is particular to this module is making them and reading a connection's peer credentials.

A path is handed to the operating system as Path.toPosixString prints it. That text must be at most 107 bytes of UTF-8 on Linux and 103 on macOS. On the native backend under Windows, connecting and listening fail with Socket.NotSupported.

Since the address is a path, connecting and listening fail the way a file operation on that path would: with Socket.AddressPathFailed and the FileSystem.ErrorKind the path would have there, such as NoSuchFileOrDirectory, NotADirectory, LinkLoop, ReadOnlyFileSystem, PathTooLong (a path over the length limit) or PermissionDenied. The failures that describe the socket rather than the path keep their own kinds: Socket.AddressInUse when something already exists at the path, and Socket.ConnectionRefused when nobody listens on it.

The socket file's path is the only name a connection has. On the side that connected it is the remote endpoint, and on the side that accepted it is the local endpoint, in both cases as the path Path.toPosixString prints, parsed again; the other endpoint is the empty path, Path.empty.

A Unix domain connection can also tell who is at the other end: its peer credentials are the process id, user id and group id of the program on the other side, which peerCredentials reads.

Connecting

connect : Path -> Task Error Connection

Connects to the listener whose socket file is at path.

Fails with:

  • Socket.AddressPathFailed FileSystem.NoSuchFileOrDirectory when nothing exists at the path, and AddressPathFailed with another FileSystem.ErrorKind when the path cannot be used, for example PermissionDenied without write permission on the socket file, NotADirectory when a part of the path is not a directory, or PathTooLong;
  • Socket.ConnectionRefused when nobody listens on it;
  • on Linux, Socket.ListenQueueFull when the operating system's queue of connections waiting on that listener is full.

The connection's remote endpoint is path, printed and parsed again, and its local endpoint is the empty path.

Listening

type alias ListenOptions =
    { path : Path, removeExisting : Bool, permissions : Maybe Int }

The options for listen: where to create the socket file, whether a socket file already there may be replaced, and the mode to give it.

removeExisting removes a socket file already at path before listening, whether or not a program still listens on it. Any other kind of file there, a symbolic link included, still makes listen fail with Socket.AddressInUse.

permissions is the socket file's mode as a number (0o600 is 384), of which only the low twelve bits, 0o7777, are used. On the native backend it is set before the socket starts listening, so no client connects before it applies; on the JavaScript backend it is set once listening has started, before the task succeeds. Nothing, or a negative number, leaves the mode the process's umask gives. If the mode cannot be set, listen fails.

defaultListenOptions : Path -> ListenOptions

Returns the options for listening at path with which listen fails when anything already exists there, and which leave the socket file's mode to the umask.

listen : ListenOptions -> Task Error Listener

Starts listening at options.path, creating the socket file there.

Fails with:

  • Socket.AddressInUse when something already exists at the path and removeExisting does not allow it to be removed;
  • Socket.AddressPathFailed when the path cannot be used, with the FileSystem.ErrorKind it would have there: for example NoSuchFileOrDirectory when a parent directory does not exist, NotADirectory when a part of the path is not a directory, PermissionDenied without write permission on the directory, ReadOnlyFileSystem, or PathTooLong.

A listen that fails after creating the socket file removes it again, and so does Socket.closeListener.

Each connection the listener hands out has as its local endpoint the path printed and parsed again, and the empty path as its remote endpoint.

Peer credentials

peerCredentials :
    Connection
    -> Task Error { pid : Int, uid : Int, gid : Int }

Asks for the peer credentials of a Unix domain connection: the process id, user id and group id of the program at the other end, as the operating system reported them when the connection was made.

Fails with Socket.InvalidInput for a connection that is not a Unix domain connection, and on the JavaScript backend for one that has closed. Otherwise it fails with Socket.NotSupported where the credentials cannot be read: on the JavaScript backend for every Unix domain connection, and on the native backend on systems other than Linux and macOS.