Prevent daemon sudden termination (SIGKILL only) upon restart/shutdown

I'm running daemon service under macOS Sierra and High-Sierra, and get unexpected behavior upon shutdown.

according to the manual of daemon life cycle (https://developer.apple.com/library/content/documentation/MacOSX/Conceptual/BPSystemStartup/Chapters/Lifecycle.html),

"If the system is being shut down or restarted, it sends a SIGTERM signal to all daemons, followed a few seconds later by SIGKILL signal"


However, in my case I see that my daemon only gets SIGKILL from `launchd` (my signal handler callback function for SIGTERM isn't being called)

I've verified that I doesn't allow sudden termination by setting the following fields in plist :

<key>NSSupportsSuddenTermination</key>

<false/>

<key>EnableTransactions</key>

<false/>


Any idea what am I missing here ?

Replies

This is working for me. Here’s what I did:

  1. I created a small Swift daemon that logs when it gets a

    SIGTERM
    . The code is pasted in at the end. Note that this logs by creating a file in
    /var/log/DaemonTermination/
    , to avoid any complications with the system’s logging subsystem. Also, I use a dispatch event source for my signal handling because that allows me to doing things that are not async signal safe (most notably, write in Swift!).
  2. I created a matching launchd property list, also pasted in below.

    IMPORTANT I did not include

    EnableTransactions
    because that defaults to false. Also,
    NSSupportsSuddenTermination
    is not a launchd job key (it’s for Cocoa apps) and thus doesn’t belong in a launchd property list.
  3. On a victim (virtual) machine running 10.13, I created the destination log directory:

    $ sudo mkdir /var/log/DaemonTermination

    .

  4. I then installed the daemon and its property list:

    $ sudo cp DaemonTermination /Library/PrivilegedHelperTools/
    $ sudo cp com.example.apple-samplecode.DaemonTermination.plist /Library/LaunchDaemons/

    .

  5. And then loaded and started the daemon:

    $ sudo launchctl load -w /Library/LaunchDaemons/com.example.apple-samplecode.DaemonTermination.plist
    $ sudo launchctl start com.example.apple-samplecode.DaemonTermination

    .

  6. I then restarted the machine using the Finder.

  7. After the restart I looked in

    /var/log/DaemonTermination/
    and, lo!, there is one of my log files:
    $ ls -lh /var/log/DaemonTermination/
    total 8
    -rw-r--r--@ 1 root  wheel    25B Dec  6 01:52 DFEE5FCC-4DE0-49C2-9D85-51F4151BCA3E.txt
    $ cat /var/log/DaemonTermination/DFEE5FCC-4DE0-49C2-9D85-51F4151BCA3E.txt 
    2017-12-06 09:52:46 +0000

    .

I’m not sure what’s going on with your setup but this should give a good starting point for your investigations.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"
import Foundation

func main() {
    let tempDir = URL(fileURLWithPath: "/var/log/DaemonTermination")
    let termFile = tempDir.appendingPathComponent("\(UUID().uuidString).txt")
    NSLog("termFile: %@", termFile.path)

    signal(SIGTERM, SIG_IGN)

    let termSource = DispatchSource.makeSignalSource(signal: SIGTERM)
    termSource.setEventHandler {
        try? Date().description.write(to: termFile, atomically: false, encoding: .utf8)
        NSLog("SIGTERM")
        exit(123)
    }
    termSource.resume()

    dispatchMain()
}

main()
exit(EXIT_FAILURE)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>com.example.apple-samplecode.DaemonTermination</string>
    <key>ProgramArguments</key>
    <array>
        <string>/Library/PrivilegedHelperTools/DaemonTermination</string>
    </array>
</dict>
</plist>