packages feed

sshtun-1.0.0: src/main.hs

-- Copyright: 2012 Dino Morelli
-- License: BSD3 (see LICENSE)
-- Author: Dino Morelli <dino@ui3.info>

import Control.Concurrent
import Control.Concurrent.STM
import Control.Monad
import Data.Maybe
import System.Directory
import System.Environment
import System.Exit
import System.Posix.Daemonize (CreateDaemon(..), serviced, simpleDaemon)
import System.Posix.Signals
import System.Posix.User
import Text.Printf

import Paths_sshtun
import Sshtun.Common
import Sshtun.Conf
import Sshtun.Log
import Sshtun.Switch
import Sshtun.Tunnel


confPath :: FilePath
confPath = "/etc/sshtun.conf"


main :: IO ()
main = do
   checkEnv

   shared <- atomically $ newTVar (Stopped, Stop)

   mapM_ ( \signal -> installHandler signal
      (Catch $ handler shared) Nothing ) [sigINT, sigTERM]

   readFile confPath >>= parseConf >>= either exitFail
      (\c -> startDaemon (localDaemonUser c) $ sshtunMain c shared)


exitFail :: String -> IO ()
exitFail msg = do
   instDocPath <- getDataFileName "README"
   _ <- printf "%s\n\nPlease see %s\n" msg instDocPath

   exitWith $ ExitFailure 1


sshtunMain :: Conf -> TVar Shared -> () -> IO ()
sshtunMain conf shared _ = do
   initLogging (logFile conf) (logPriority conf)

   logM NOTICE "sshtun starting"

   -- Only start the Switch Watcher thread if the user has configured
   -- it, otherwise, we run all the time
   if (isJust . switchUrl $ conf)
      then do
         _ <- forkIO $ switchWatcher conf shared
         return ()
      else run shared

   tunnelStart conf shared


startDaemon :: String -> (() -> IO ()) -> IO ()
startDaemon localUser p = serviced $ simpleDaemon
   { program = p
   , user = Just localUser
   }


handler :: TVar Shared -> IO ()
handler shared = do
   logM NOTICE "sshtun stopping"
   stop shared


checkEnv :: IO ()
checkEnv = do
   -- root user check
   euid <- getEffectiveUserID
   when (euid /= 0) $
      exitFail "This service must be run by the root user"

   -- conf file check
   doesFileExist confPath >>= (flip unless $ do
      let msg = init . unlines $
            [ "Unable to find conf file at " ++ confPath
            , "This could mean sshtun isn't installed fully"
            ]
      exitFail msg
      )

   -- args check
   args <- getArgs
   when (
      (not $ null args) &&
      (not $ head args `elem` ["start", "stop", "restart"])
      ) $ exitFail "usage: sshtun {start|stop|restart}"