packages feed

hdaemonize (empty) → 0.1

raw patch · 4 files changed

+226/−0 lines, 4 filesdep +basedep +haskell98dep +hsyslogsetup-changed

Dependencies added: base, haskell98, hsyslog, unix

Files

+ LICENSE view
@@ -0,0 +1,28 @@+Copyright (c) 2009, Anton Tayanovskyy+All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are+met:++    * Redistributions of source code must retain the above copyright+      notice, this list of conditions and the following disclaimer.+    * Redistributions in binary form must reproduce the above +      copyright notice, this list of conditions and the following +      disclaimer in the documentation and/or other materials provided+      with the distribution.+    * Neither the name of Anton Tayanovskyy nor the names of the+      contributors may be used to endorse or promote products derived +      from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Setup.lhs view
@@ -0,0 +1,4 @@+#!/usr/bin/env runhaskell++> import Distribution.Simple+> main = defaultMain
+ System/Posix/Daemonize.hs view
@@ -0,0 +1,173 @@+module System.Posix.Daemonize (Logger, Program, daemonize, serviced) where+      +{- originally based on code from +   http://sneakymustard.com/2008/12/11/haskell-daemons -}++import Control.Concurrent+import Control.Exception+import Prelude hiding (catch)+import System+import System.Exit+import System.Posix+import System.Posix.Syslog+++-- | The simplest possible interface to syslog.+type Logger = String -> IO ()+++-- | A program is any IO computation. It also accepts a syslog handle.+type Program = Logger -> IO ()+++-- | Daemonizes a given IO computation by forking twice, closing+--   standard file descriptors, blocking sigHUP, setting file creation+--   mask, starting a new session, and changing the working directory+--   to root.++daemonize :: IO () ->  IO () +daemonize program = ++    do setFileCreationMask 0 +       forkProcess p+       exitSuccess++    where++      p  = do createSession+              forkProcess p'+              exitSuccess+                              +      p' = do changeWorkingDirectory "/"+              closeFileDescriptors+              blockSignal sigHUP+              program+++-- | Turns a program into a UNIX daemon (system service) ready to be+--   deployed to /etc/rc.d or similar startup folder.  The resulting+--   program handles command-line arguments (start, stop, or restart).+--+--   With start option it writes out a PID to /var/run/$name.pid where+--   $name is the executable name. If PID already exists, it refuses+--   to start, guaranteeing there is only one live instance.+--+--   With stop option it reads the PID from /var/run/$name.pid and+--   terminates the corresponding process (first a soft kill, SIGTERM,+--   then a hard kill, SIGKILL).+--+--   Another addition over the daemonize function is dropping+--   privileges.  If a system user and group with a name that matches+--   the executable name exist, privileges are dropped to that user and+--   group.  Otherwise, they are dropped to the standard daemon user+--   and group.+--+--   Finally, exceptions in the program are caught, logged to syslog,+--   and the program restarted.++serviced :: Program -> IO ()+serviced program = do name <- getProgName+                      args <- getArgs+                      process name args+    where++      program' name = withSyslog name [] DAEMON $+                      do let log = syslog Notice+                         log "starting"+                         pidWrite name+                         dropPrivileges name+                         forever log program++      process name ["start"] = pidExists name >>= f where+          f True  = do error "PID file exists. Process already running?"+                       exitFailure+          f False = daemonize (program' name)+                 +      process name ["stop"]  = +          do pid <- pidRead name+             let ifdo x f = x >>= \x -> if x then f else pass+             case pid of+               Nothing  -> pass+               Just pid -> +                   (do signalProcess sigTERM pid+                       usleep (10^6)+                       ifdo (pidLive pid) $ +                            do usleep (3*10^6)+                               ifdo (pidLive pid) (signalProcess sigKILL pid))+                   `finally`+                   removeLink (pidFile name)++      process name ["restart"] = do process name ["stop"]+                                    process name ["start"]+      process name _ = +          putStrLn $ "usage: " ++ name ++ " {start|stop|restart}"+++{- implementation -}++forever :: Logger -> Program -> IO ()+forever log program =     +    program log `catch` restart where+        restart :: SomeException -> IO () +        restart e = +            do log ("unexpected exception: " ++ show e)+               log "restarting in 5 seconds"+               usleep (5 * 10^6)+               forever log program++closeFileDescriptors :: IO ()+closeFileDescriptors = +    do null <- openFd "/dev/null" ReadWrite Nothing defaultFileFlags+       let sendTo fd' fd = closeFd fd >> dupTo fd' fd+       mapM_ (sendTo null) $ [stdInput, stdOutput, stdError]++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+        f :: Either IOException GroupID -> Maybe GroupID+        f (Left e)    = Nothing+        f (Right gid) = Just gid++getUserID :: String -> IO (Maybe UserID)+getUserID user = +    try (fmap userID (getUserEntryForName user)) >>= return . f where+        f :: Either IOException UserID -> Maybe UserID+        f (Left e)    = Nothing+        f (Right uid) = Just uid++dropPrivileges :: String -> IO ()+dropPrivileges name = +    do Just ud <- getUserID "daemon"+       Just gd <- getGroupID "daemon"+       u       <- fmap (maybe ud id) $ getUserID name+       g       <- fmap (maybe gd id) $ getGroupID name+       setGroupID g +       setUserID u++pidFile:: String -> String+pidFile name = "/var/run/" ++ name ++ ".pid"++pidExists :: String -> IO Bool+pidExists name = fileExist (pidFile name)++pidRead :: String -> IO (Maybe CPid)+pidRead name = pidExists name >>= choose where+    choose True  = fmap (Just . read) $ readFile (pidFile name)+    choose False = return Nothing++pidWrite :: String -> IO ()+pidWrite name =+    getProcessID >>= \pid ->+    writeFile (pidFile name) (show pid)++pidLive :: CPid -> IO Bool+pidLive pid = +    (getProcessPriority pid >> return True) `catch` f where+        f :: IOException -> IO Bool+        f _ = return False+        +pass :: IO () +pass = return ()
+ hdaemonize.cabal view
@@ -0,0 +1,21 @@+Name:		hdaemonize+Version:	0.1+Cabal-Version:  >= 1.2+License:	BSD3+License-file:   LICENSE+Author:         Anton Tayanovskyy+Maintainer:     Anton Tayanovskyy <name.surname@gmail.com>+Homepage:       http://github.com/toyvo/hdaemonize/tree/master+Category:	System+Synopsis:       utilities for UNIX daemon writing+Description:	Provides two functions that help writing better UNIX daemons,+		daemonize and serviced: daemonize does what a daemon should do+		(forking and closing descriptors), while serviced does that and +		more (syslog interface, PID file writing, start-stop-restart +		command line handling, dropping privileges).+Build-Type:	Simple+Library+  Build-Depends:	base, unix, haskell98, hsyslog+  Exposed-modules:+    System.Posix.Daemonize+