packages feed

hdaemonize 0.5.0.2 → 0.5.1

raw patch · 3 files changed

+98/−91 lines, 3 filesdep +bytestringdep ~hsyslogPVP ok

version bump matches the API change (PVP)

Dependencies added: bytestring

Dependency ranges changed: hsyslog

API changes (from Hackage documentation)

Files

README view
@@ -1,8 +1,8 @@-`hdaemonize-0.4.3`+`hdaemonize` =================  `hdaemonize` is a simple library that hides some of the complexities-of writing UNIX daemons in Haskell.  +of writing UNIX daemons in Haskell.  Obtaining -----------@@ -42,7 +42,7 @@     # mydaemon start  starts the service. A second call to start will complain that the-program is already running. +program is already running.  During its execution, mydaemon will simply write a new number to `/tmp/counter` every second, until it reaches 5. Then, an exception@@ -58,7 +58,7 @@      # mydaemon stop     # mydaemon restart- + Finally, `mydaemon` drops privileges.  By default it changes the effective user and group ids to those of the `daemon` user, but it prefers to use those of `mydaemon`, if present.@@ -66,6 +66,9 @@  Changelog ---------++* 0.5.1+    * Updated to use hsyslog >=4  * 0.4     * added support for a privileged action before dropping privileges
System/Posix/Daemonize.hs view
@@ -1,13 +1,15 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RankNTypes #-} module System.Posix.Daemonize (   -- * Simple daemonization-  daemonize, +  daemonize,   -- * Building system services   serviced, CreateDaemon(..), simpleDaemon,-  -- * Intradaemon utilities                              +  -- * Intradaemon utilities   fatalError, exitCleanly-  -- * An example                              -  --                               +  -- * An example+  --   -- | Here is an example of a full program which writes a message to   -- syslog once a second proclaiming its continued existance, and   -- which installs its own SIGHUP handler.  Note that you won't@@ -15,33 +17,34 @@   -- systems.  @syslogd@ detects repeated messages and prints the   -- first one, then delays for the rest and eventually writes a line   -- about how many times it has seen it.-  --                               +  --+  -- > {-# LANGUAGE OverloadedStrings #-}   -- > module Main where   -- >   -- > import System.Posix.Daemonize (CreateDaemon(..), serviced, simpleDaemon)   -- > import System.Posix.Signals (installHandler, Handler(Catch), sigHUP, fullSignalSet)-  -- > import System.Posix.Syslog (syslog, Priority(Notice))+  -- > import System.Posix.Syslog (syslogUnsafe, Facility(DAEMON), Priority(Notice))   -- > import Control.Concurrent (threadDelay)   -- > import Control.Monad (forever)-  -- > +  -- >   -- > main :: IO ()   -- > main = serviced stillAlive-  -- > +  -- >   -- > stillAlive :: CreateDaemon ()   -- > stillAlive = simpleDaemon { program = stillAliveMain }-  -- > +  -- >   -- > stillAliveMain :: () -> IO ()   -- > stillAliveMain _ = do   -- >   installHandler sigHUP (Catch taunt) (Just fullSignalSet)   -- >   forever $ do threadDelay (10^6)-  -- >                syslog Notice "I'm still alive!"-  -- >                +  -- >                syslog DAEMON Notice "I'm still alive!"+  -- >   -- > taunt :: IO ()-  -- > taunt = syslog Notice "I sneeze in your general direction, you and your SIGHUP."+  -- > taunt = syslogUnsafe DAEMON Notice "I sneeze in your general direction, you and your SIGHUP."    ) where-      -{- originally based on code from ++{- originally based on code from    http://sneakymustard.com/2008/12/11/haskell-daemons -}  @@ -56,13 +59,21 @@ import Prelude hiding (catch) #endif +#if !(MIN_VERSION_base(4,8,0))+import Control.Applicative ((<$))+#endif++import Data.ByteString (ByteString)+import qualified Data.ByteString.Char8 as ByteString+import Data.Maybe (isNothing, fromMaybe, fromJust) import System.Environment import System.Exit import System.Posix-import System.Posix.Syslog (withSyslog,Option(..),Priority(..),Facility(..),syslog)+import System.Posix.Syslog (withSyslog,SyslogConfig(..),Option(..),Priority(..),PriorityMask(..),Facility(..),syslogUnsafe) import System.FilePath.Posix (joinPath)-import Data.Maybe (isNothing, fromMaybe, fromJust) +syslog :: Priority -> ByteString -> IO ()+syslog = syslogUnsafe DAEMON  -- | Turning a process into a daemon involves a fixed set of -- operations on unix systems, described in section 13.3 of Stevens@@ -70,36 +81,32 @@ -- they are fixed, they can be written as a single function, -- 'daemonize' taking an 'IO' action which represents the daemon's -- actual activity.--- +-- -- Briefly, 'daemonize' sets the file creation mask to 0, forks twice, -- changed the working directory to @/@, closes stdin, stdout, and -- stderr, blocks 'sigHUP', and runs its argument.  Strictly, it -- should close all open file descriptors, but this is not possible in -- a sensible way in Haskell.--- +-- -- The most trivial daemon would be--- +-- -- > daemonize (forever $ return ())--- +-- -- which does nothing until killed. -daemonize :: IO () -> IO () -daemonize program = -    -  do setFileCreationMask 0 -     forkProcess p-     exitImmediately ExitSuccess-+daemonize :: IO () -> IO ()+daemonize program = do+        setFileCreationMask 0+        forkProcess p+        exitImmediately ExitSuccess     where-       p  = do createSession               forkProcess p'               exitImmediately ExitSuccess-                                     p' = do changeWorkingDirectory "/"               closeFileDescriptors               blockSignal sigHUP-              program +              program   @@ -107,9 +114,9 @@ -- | 'serviced' turns a program into a UNIX daemon (system service) --   ready to be deployed to /etc/rc.d or similar startup folder.  It --   is meant to be used in the @main@ function of a program, such as--- +-- -- > serviced simpleDaemon--- +-- --   The resulting program takes one of three arguments: @start@, --   @stop@, and @restart@.  All control the status of a daemon by --   looking for a file containing a text string holding the PID of@@ -125,9 +132,9 @@ --   written therein.  First it does a soft kill, SIGTERM, giving the --   daemon a chance to shut down cleanly, then three seconds later a --   hard kill which the daemon cannot catch or escape.--- +-- --   @restart@ is simple @stop@ followed by @start@.--- +-- --   'serviced' also tries to drop privileges.  If you don't specify a --   user the daemon should run as, it will try to switch to a user --   with the same name as the daemon, and otherwise to user @daemon@.@@ -135,44 +142,40 @@ --   matters, the name of the daemon is by default the name of the --   executable file, but can again be set to something else in the --   'CreateDaemon' record.--- +-- --   Finally, exceptions in the program are caught, logged to syslog, --   and the program restarted.  serviced :: CreateDaemon a -> IO ()-serviced daemon = do -  systemName <- getProgName-  let daemon' = daemon { name = if isNothing (name daemon) -                                then Just systemName else name daemon }-  args <- getArgs-  process daemon' args+serviced daemon = do+        systemName <- getProgName+        let daemon' = daemon { name = if isNothing (name daemon)+                                        then Just systemName else name daemon }+        args <- getArgs+        process daemon' args     where-#if MIN_VERSION_hsyslog(2,0,0)-      program' daemon = withSyslog (fromJust $ name daemon) (syslogOptions daemon) DAEMON [] $-#else-      program' daemon = withSyslog (fromJust $ name daemon) (syslogOptions daemon) DAEMON $-#endif+      program' daemon = withSyslog (SyslogConfig (ByteString.pack $ fromJust $ name daemon) (syslogOptions daemon) DAEMON NoMask) $ \_ ->                       do let log = syslog Notice                          log "starting"                          pidWrite daemon                          privVal <- privilegedAction daemon                          dropPrivileges daemon-                         forever $ program daemon $ privVal+                         forever $ program daemon privVal        process daemon ["start"] = pidExists daemon >>= f where           f True  = do error "PID file exists. Process already running?"                        exitImmediately (ExitFailure 1)           f False = daemonize (program' daemon)-                 -      process daemon ["stop"]  = ++      process daemon ["stop"]  =           do pid <- pidRead daemon              case pid of                Nothing  -> pass-               Just pid -> -                   (do whenM (pidLive pid) $-                            do signalProcess sigTERM pid-                               usleep (10^3)-                               wait (killWait daemon) pid)+               Just pid ->+                   whenM (pidLive pid)+                            (do signalProcess sigTERM pid+                                usleep (10^3)+                                wait (killWait daemon) pid)                    `finally`                    removeLink (pidFile daemon) @@ -183,13 +186,13 @@         f True =           do pid <- pidRead daemon              case pid of-               Nothing -> putStrLn $ (fromJust $ name daemon) ++ " is not running."+               Nothing -> putStrLn $ fromJust (name daemon) ++ " is not running."                Just pid ->                  do res <- pidLive pid                     if res then-                      do putStrLn $ (fromJust $ name daemon) ++ " is running."-                         else putStrLn $ (fromJust $ name daemon) ++ " is not running, but pidfile is remaining."-        f False = putStrLn $ (fromJust $ name daemon) ++ " is not running."+                              putStrLn $ fromJust (name daemon) ++ " is running."+                         else putStrLn $ fromJust (name daemon) ++ " is not running, but pidfile is remaining."+        f False = putStrLn $ fromJust (name daemon) ++ " is not running."        process _      _ =         getProgName >>= \pname -> putStrLn $ "usage: " ++ pname ++ " {start|stop|status|restart}"@@ -254,8 +257,8 @@                         -- wait forever.  Default 4. } --- | The simplest possible instance of 'CreateDaemon' is --- +-- | The simplest possible instance of 'CreateDaemon' is+-- -- > CreateDaemon { -- >  privilegedAction = return () -- >  program = const $ forever $ return ()@@ -265,7 +268,7 @@ -- >  syslogOptions = [], -- >  pidfileDirectory = Nothing, -- > }--- +-- -- which does nothing forever with all default settings.  We give it a -- name, 'simpleDaemon', since you may want to use it as a template -- and modify only the fields that you need.@@ -281,58 +284,60 @@   privilegedAction = return (),   killWait = Just 4 }-     + {- implementation -}  forever :: IO () -> IO ()-forever program =     +forever program =     program `catch` restart where-        restart :: SomeException -> IO () -        restart e = -            do syslog Error ("unexpected exception: " ++ show e)+        restart :: SomeException -> IO ()+        restart e =+            do syslog Error $ ByteString.pack ("unexpected exception: " ++ show e)                syslog Error "restarting in 5 seconds"                usleep (5 * 10^6)                forever program  closeFileDescriptors :: IO ()-closeFileDescriptors = +closeFileDescriptors =     do null <- openFd "/dev/null" ReadWrite Nothing defaultFileFlags        let sendTo fd' fd = closeFd fd >> dupTo fd' fd-       mapM_ (sendTo null) $ [stdInput, stdOutput, stdError]+       mapM_ (sendTo null) [stdInput, stdOutput, stdError] -blockSignal :: Signal -> IO () +blockSignal :: Signal -> IO () blockSignal sig = installHandler sig Ignore Nothing >> pass  getGroupID :: String -> IO (Maybe GroupID)-getGroupID group = -    try (fmap groupID (getGroupEntryForName group)) >>= return . f where+getGroupID group =+        f <$> try (fmap groupID (getGroupEntryForName group))+    where         f :: Either IOException GroupID -> Maybe GroupID         f (Left _)    = Nothing         f (Right gid) = Just gid  getUserID :: String -> IO (Maybe UserID)-getUserID user = -    try (fmap userID (getUserEntryForName user)) >>= return . f where+getUserID user =+        f <$> try (fmap userID (getUserEntryForName user))+    where         f :: Either IOException UserID -> Maybe UserID         f (Left _)    = Nothing         f (Right uid) = Just uid  dropPrivileges :: CreateDaemon a -> IO ()-dropPrivileges daemon = +dropPrivileges daemon =     do Just ud <- getUserID "daemon"        Just gd <- getGroupID "daemon"        let targetUser = fromMaybe (fromJust $ name daemon) (user daemon)            targetGroup = fromMaybe (fromJust $ name daemon) (group daemon)-       u       <- fmap (maybe ud id) $ getUserID targetUser-       g       <- fmap (maybe gd id) $ getGroupID targetGroup-       setGroupID g +       u <- fromMaybe ud <$> getUserID targetUser+       g <- fromMaybe gd <$> getGroupID targetGroup+       setGroupID g        setUserID u  pidFile:: CreateDaemon a -> String-pidFile daemon = joinPath [dir, (fromJust $ name daemon) ++ ".pid"]+pidFile daemon = joinPath [dir, fromJust (name daemon) ++ ".pid"]   where dir = fromMaybe "/var/run" (pidfileDirectory daemon)  pidExists :: CreateDaemon a -> IO Bool@@ -340,7 +345,7 @@  pidRead :: CreateDaemon a -> IO (Maybe CPid) pidRead daemon = pidExists daemon >>= choose where-    choose True  = fmap (Just . read) $ readFile (pidFile daemon)+    choose True  = return . read <$> readFile (pidFile daemon)     choose False = return Nothing  pidWrite :: CreateDaemon a -> IO ()@@ -349,12 +354,12 @@     writeFile (pidFile daemon) (show pid)  pidLive :: CPid -> IO Bool-pidLive pid = +pidLive pid =     (getProcessPriority pid >> return True) `catch` f where         f :: IOException -> IO Bool         f _ = return False-        -pass :: IO () ++pass :: IO () pass = return ()  -- | When you encounter an error where the only sane way to handle it@@ -363,7 +368,7 @@ -- configuration files on startup. fatalError :: MonadIO m => String -> m a fatalError msg = liftIO $ do-  syslog Error $ "Terminating from error: " ++ msg+  syslog Error $ ByteString.pack $ "Terminating from error: " ++ msg   exitImmediately (ExitFailure 1)   undefined -- You will never reach this; it's there to make the type checker happy 
hdaemonize.cabal view
@@ -1,5 +1,5 @@ Name:		hdaemonize-Version:	0.5.0.2+Version:	0.5.1 Cabal-Version:  >= 1.6 License:	BSD3 License-file:   LICENSE@@ -18,15 +18,14 @@  Library   Build-Depends:    base >= 4 && < 5+                  , bytestring                   , unix-                  , hsyslog < 3+                  , hsyslog >= 4                   , extensible-exceptions                   , filepath                   , mtl   Exposed-modules:      System.Posix.Daemonize-  Extensions:           CPP   if impl(ghc > 6.12)       Ghc-Options:      -Wall -fno-warn-unused-do-bind -fno-warn-type-defaults -fno-warn-name-shadowing   else       Ghc-Options:      -Wall -fno-warn-type-defaults -fno-warn-name-shadowing-