about summary refs log tree commit diff
diff options
context:
space:
mode:
authorsternenseemann <0rpkxez4ksa01gb3typccl0i@systemli.org>2021-03-15 01:17:26 +0100
committersterni <sternenseemann@systemli.org>2021-03-15 01:47:32 +0100
commitaa8a75a877bfc2c177aabe5eef8103304fab77c7 (patch)
tree476816a5192b4c87566856b45590f937edeb846a
parentb3ec2aba0c3aabae7755d8174eb4444b27000d91 (diff)
refactor(Spacecookie.Systemd): move {to,from}Fd to top-level
Move file descriptor conversion into top level bindings, throw
IOExceptions on errors and document them.

SystemdException no longer contains the invalid file descriptor
exception.
-rw-r--r--server/Network/Spacecookie/Systemd.hs58
1 files changed, 37 insertions, 21 deletions
diff --git a/server/Network/Spacecookie/Systemd.hs b/server/Network/Spacecookie/Systemd.hs
index d43690e..22ab0aa 100644
--- a/server/Network/Spacecookie/Systemd.hs
+++ b/server/Network/Spacecookie/Systemd.hs
@@ -10,11 +10,12 @@ module Network.Spacecookie.Systemd
 
 import Control.Concurrent.MVar (newMVar, takeMVar, mkWeakMVar)
 import Control.Exception.Base
-import Control.Monad (when, void)
+import Control.Monad (when)
 import Data.Maybe (fromMaybe)
 import Foreign.C.Types (CInt (..))
 import GHC.Conc (closeFdWith)
 import Network.Gopher
+import System.IO.Error (mkIOError, userErrorType)
 import System.Posix.Types (Fd (..))
 import System.Socket hiding (Error (..))
 import System.Socket.Family.Inet6
@@ -27,14 +28,42 @@ import System.Systemd.Daemon.Fd (storeFd, getActivatedSockets)
 foreign import ccall unsafe "close"
   c_close :: CInt -> IO CInt
 
--- TODO Check Socket type, ...
-data SystemdException = IncorrectNum | InvalidFd
+-- | Close a 'Fd' using close(1). Throws an 'IOException' on error.
+closeFd :: Fd -> IO ()
+closeFd fd = do
+  res <- c_close $ fromIntegral fd
+  when (res /= 0) $ throwIO
+    $ mkIOError userErrorType "Could not close File Descriptor" Nothing Nothing
+
+-- | Irreversibly convert a 'Socket' into an 'Fd'.
+toFd :: Socket a b c -> IO Fd
+toFd (Socket mvar) = fmap (Fd . fromIntegral) (takeMVar mvar)
+
+-- | Create an 'Socket' from an 'Fd'. This action is unsafe
+--   since the type of the socket is not checked meaning that
+--   whatever type the resulting 'Socket' has is not guaranteed
+--   to be the same as its type indicates. Thus, this function
+--   needs to be used with care so the safety guarantees of
+--   'Socket' are not violated.
+--
+--   Throws an 'IOException' if the 'Fd' is invalid.
+fromFd :: Fd -> IO (Socket a b c)
+fromFd fd = do
+  -- TODO Validate socket type
+  when (fd < 0) $ throwIO
+    $ mkIOError userErrorType "Invalid File Descriptor" Nothing Nothing
+  mfd <- newMVar (fromIntegral fd)
+  let s = Socket mfd
+  _ <- mkWeakMVar mfd (close s)
+  pure s
+
+data SystemdException
+  = IncorrectNum
   deriving (Eq, Ord)
 
+instance Exception SystemdException
 instance Show SystemdException where
   show IncorrectNum = "SystemdException: Only exactly one Socket is supported"
-  show InvalidFd    = "SystemdException: Invalid File Descriptor received"
-instance Exception SystemdException
 
 systemdSocket :: GopherConfig -> IO (Socket Inet6 Stream TCP)
 systemdSocket cfg = getActivatedSockets >>= \sockets ->
@@ -42,20 +71,9 @@ systemdSocket cfg = getActivatedSockets >>= \sockets ->
     Nothing -> setupGopherSocket cfg
     Just [fd] -> do
       listenWarning
-      toSocket fd
+      fromFd fd
     Just _ -> throwIO IncorrectNum
-  where toSocket :: Fd -> IO (Socket Inet6 Stream TCP)
-        toSocket fd = do
-          when (fd < 0) $ throwIO InvalidFd
-          mfd <- newMVar (fromIntegral fd)
-          let s = Socket mfd
-          _ <- mkWeakMVar mfd (close s)
-          pure s
-        -- logs a warning that a listen addr which
-        -- may differ from the listen addr of the
-        -- systemd socket is specified if it is
-        -- necessary and possible
-        listenWarning = fromMaybe (pure ()) $ do
+  where listenWarning = fromMaybe (pure ()) $ do
           logAction <- cLogHandler cfg
           addr <- cListenAddr cfg
           pure . logAction GopherLogLevelWarn
@@ -70,6 +88,4 @@ systemdStoreOrClose s = do
   res <- storeFd fd
   case res of
     Just () -> return ()
-    Nothing -> closeFdWith (void . c_close . fromIntegral) fd
-  where toFd :: Socket Inet6 Stream TCP -> IO Fd
-        toFd (Socket mvar) = fmap (Fd . fromIntegral) (takeMVar mvar)
+    Nothing -> closeFdWith closeFd fd