iAchieved.it

Software Development Tips and Tricks

By

Trapping Signals with Swift on Linux

Update: Folks have figured out how to use sigaction which is the preferred way of catching a signal. Jump down to read all about it, both signal and sigaction methods work!

Unix applications frequently have a need to catch, or trap, signals. For example, many daemons will reread their configuration file upon receipt of the SIGHUP signal. A client interface may catch CTRL-C and print Are you sure you want to quit? [yN].

This can be accomplished in Swift on Linux as well by using the signal function from Glibc. The code, which is a play on Adam Sharp’s gist:

Run with swift or compile and execute using swiftc. The application will wait at the select statement. Pressing CTRL-C will generate a SIGINT and the trap routine will execute (and end the application).

To be clear, the application did not exit because the trap closure was executed. The exit occurred because flow of control transferred from the closure to after the select. Think of the line after the select as an implicit exit(0). If you rewrite this as

then the application will not exit after the signal handler executes.

Note: The Signal enumeration is but a small subset of the various signals. Check out man 7 signal for a complete list.

Pretty Printing

There’s no need to add another integer-to-string translation table to get the type of signal received. strsignal is a handy function to get a human-friendly string name for the signal:

Trailing Closures

Trailing closures are one of those syntactical goodies that make Swift a joy to program in. Your first exposure to them is probably implementing a callback function to an HTTP GET request:

The code between the braces is actually the last parameter to the HTTPRequest class initializer. The parameter signature is likely to be (HTTPResponse, NSError) -> Void. Nothing requires you to code this in the form of the trailing closure, but it’s frequently nice to. Just remember that a trailing closure can only be used when the function to be called is the last parameter in the function signature. Those are the rules.

We of course use this feature in our routine:

The trailing closure syntax is used to specify the signal handler which must adhere to the __signalhanlder_t function signature. This signature is simply (Int32) -> Void, which is what our trailing closure is.

What about sigaction?

Truth be told, we ought to be using sigaction rather than signal. The man page says so, and everyone knows better than to argue with a man page.

The behavior of signal() varies across UNIX versions, and has also varied historically across different versions of Linux.   Avoid  its  use:  use sigaction(2) instead.

Unfortunately I haven’t figured out how to use sigaction with Swift on Linux. There are two sigaction definitions in Glibc. One is for the sigaction structure (struct sigaction) and the other is for the sigaction function. Swift can’t seem to distinguish between the two, and only the function is surfaced. Without the structure definition its difficult to call sigaction. Perhaps it is possible to write our own struct sigaction definition and somehow trick Swift into thinking it can be passed into the sigaction function. I haven’t tried, but if someone out there figures it out, leave a comment!

Hold the phone! An update on sigaction from Robert (see comments) allows us to use sigaction! We’ve taken his code (thanks Robert) and updated it a bit:

Try it out!

One Last Example

I mentioned above that the trailing closure didn’t have to be used to provide the signal handler routine. In this code we declare a single function sighandler with a (Int32) -> Void signature and then use it to trap SIGINT, SIGHUP, and SIGUSR1.

Handling Signals

Handling Signals

5 Responses to Trapping Signals with Swift on Linux

  1. Robert says:

    You don’t need to do any tricking, “var action = sigaction()” will create a sigaction struct straight out of the box. Just tested it on Linux!

    • Robert says:

      Oh, but then you run into the problem that there’s a union involved. Which are supposed to be imported correctly these days, I thought, but seem to be giving me trouble.

    • Robert says:

      “unsafeBitCast” to the rescue:

      It works perfectly.

      • Joe says:

        Robert, thanks! I’ll test and add to the post. If you have a Twitter handle to include for attribution feel free to share, I want to make sure folks are recognized for all of the knowledge we’re building up running with Swift on Linux.

Leave a Reply

Your email address will not be published. Required fields are marked *