How can I turn either a Unix POSIX file descriptor or standard input Handle into a Socket?
-
21-06-2021 - |
سؤال
In inetd and systemd type systems, it is possible for the system to bind a socket and launch the application with the socket already existing, for example to provide socket based service starting. I would like to take advantage of this functionality in one of my Haskell daemons.
The daemon currently calls, socket
, bindSocket
, listen
to create a Socket
object that I can later call accept
on. To change this to an inetd
type system I would need to use standard input as a Socket
, but all I can find so far is stdin :: Handle
, or fdToHandle :: CInt -> Handle
- neither are quite what I need.
I can't seem to find anything that has type Handle -> Socket
, nor anything that is like stdin :: Socket
. The nearest I can find is mkSocket
which is very low-level, and most other languages (ie, Ruby) provide a call to turn a file descriptor into a socket without having to specify various other parameters.
المحلول
C applications have the luxury of having sd-daemon.h
which handles socket passing automatically. In Haskell, this file has to be emulated manually.
You can use stdInput
to get the stdin
file descriptor. The handleToFd
function can of course also be used. Since you want POSIX-specific behavior, you can't expect it to work on Windows, though.
Once you have the FD, you have no choice but to use the mkSocket
function. Haskell can't guess what kind of socket that you want, so you have to specify it. You most probably want:
mkSocket fd AF_UNIX Stream defaultProtocol Listening
Please remember that this isn't necessarily how systemd
passes file descriptors to your application. You must check the LISTEN_FDS
and LISTEN_PID
environment variables to see which file descriptors to use and whether its your job to even bind the sockets. The default file descriptor representing the default socket is FD 3, not FD 0 as you are assuming. systemd
might also give you multiple sockets to use if the service file demands it.
نصائح أخرى
The main trick for MkSocket is knowing you have the right parameters. The sd-daemon.h helps with this. I see from systemd man page on sd_is_fifo that it uses fstat and getsockname, the code for sd_is_socket is in git here.
You can use fstat
wrapped in the unix package to help
getFdStatus :: Fd -> IO FileStatus
isSocket :: FileStatus -> Bool
The Network package (partly using the Network.Socket.Internals) does also use the C getsockname
function (the helper withNewSockAddr
allocates the right buffer for the answer). This require either guessing the "family" or perhaps just allocating a big buffer for the answer (sockaddr_storage
from RFC 2553). But Network takes the socket file descriptor from the Socket
data. You could pull out the code from Network and re-implement the checks that the sd-daemons.h does.
Also, the getsockopt
code has probably been wrapped in the network-socket-options package.
But it does not seem anyone has put it together quite the way you need. Odd.