diff --git a/README.md b/README.md index 2b052fa..b1c8f9d 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,15 @@ Where: * `-w`, `--workdir` `DIR` - chdir to `DIR` before executing COMMAND * `-t`, `--timeout` `TIMEOUT` - timeout (in seconds) to wait for all child processes to exit +`WARNING`: by default pid1 will first send the TERM signal to it's "immediate child" process. +In most scenarios that will be the only process running but in some cases that will be the +"main" process that could have spawned it's own children. In this scenario it's prudent to shutdown +the "main" process first, since usually it has mechanisms in place to shut down it's children. If +we were to shutdown a child process before "main" was shutdown it might try to restart it. +This is why, if the "main" process doesn't exit within `timeout` we will proceed to send the TERM +signal to all processes and wait **again** for `timeout` until we finally send the KILL signal to all +processes. This is a **breaking change since 0.1.3.0**. + The recommended use case for this executable is to embed it in a Docker image. Assuming you've placed it at `/sbin/pid1`, the two commonly recommended usages are: diff --git a/pid1.cabal b/pid1.cabal index eb4c449..f7bba43 100644 --- a/pid1.cabal +++ b/pid1.cabal @@ -1,5 +1,5 @@ name: pid1 -version: 0.1.2.0 +version: 0.1.3.0 synopsis: Do signal handling and orphan reaping for Unix PID1 init processes description: Please see README.md or view Haddocks at homepage: https://github.com/fpco/pid1#readme diff --git a/src/System/Process/PID1.hs b/src/System/Process/PID1.hs index 3a9cbb6..83f0b45 100644 --- a/src/System/Process/PID1.hs +++ b/src/System/Process/PID1.hs @@ -181,9 +181,6 @@ runAsPID1 cmd args env' timeout = do -- children processes. Then start a thread waiting for that -- variable to be filled and do the actual killing. killChildrenVar <- newEmptyMVar - _ <- forkIO $ do - takeMVar killChildrenVar - killAllChildren timeout -- Helper function to start killing, used below let startKilling = void $ tryPutMVar killChildrenVar () @@ -206,6 +203,10 @@ runAsPID1 cmd args env' timeout = do ClosedHandle e -> assert False (exitWith e) OpenHandle pid -> return pid + _ <- forkIO $ do + takeMVar killChildrenVar + killAllChildren child timeout + -- Loop on reaping child processes reap startKilling child @@ -247,9 +248,20 @@ reap startKilling child = do startKilling | otherwise -> return () -killAllChildren :: Int -> IO () -killAllChildren timeout = do - -- Send all children processes the TERM signal +killAllChildren :: CPid -> Int -> IO () +killAllChildren cid timeout = do + -- Send the direct child process the TERM signal + signalProcess sigTERM cid `catch` \e -> + if isDoesNotExistError e + then return () + else throwIO e + + -- Wait for `timeout` seconds and allow the 'main' process to take care + -- of shutting down any child processes itself. + threadDelay $ timeout * 1000 * 1000 + + -- If the 'main' process did not handle shutting down the rest of the + -- child processes we will signal SIGTERM to them directly. signalProcess sigTERM (-1) `catch` \e -> if isDoesNotExistError e then return ()