33

I'm trying to write some code that will catch a signal like SIGTERM.

I found this and I also found How to handle blocking i/o in Rust, or long running external function calls in general.

But in the current Rust version (0.12 nightly) it seems like that std::io::signal::Listener was removed. Did it get put somewhere else? If so can someone point me to how to catch a signal?

Community
  • 1
  • 1
bantl23
  • 879
  • 2
  • 9
  • 8
  • This is a good overview about signals in Rust: https://vorner.github.io/2018/06/28/signal-hook.html – ceving Feb 01 '21 at 16:01

3 Answers3

28

It seems that it's now fairly trivial to implement this. The Signal handling section of Command Line Applications in Rust goes over the concept, and mentions the ctrlc crate to handle that specific signal, and the signal-hook crate to handle signals in general.

Via the guide, with signal-hook it should be as simple as:

use std::{error::Error, thread};
use signal_hook::{iterator::Signals, SIGTERM};

fn main() -> Result<(), Box<Error>> {
    let signals = Signals::new(&[SIGTERM])?;

    thread::spawn(move || {
        for sig in signals.forever() {
            println!("Received signal {:?}", sig);
        }
    });

    Ok(())
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Blue
  • 646
  • 5
  • 10
9

I believe that std::io::signal module was removed in this pull request. It is claimed that proper signals handling was never implemented properly for native runtime, so you likely wouldn't be able to use it now anyway. This seems to be a tracking issue for this problem.

In the meantime, I think, you will have to drop down to the lowest-level unsafe functions from libc.

Vladimir Matveev
  • 120,085
  • 34
  • 287
  • 296
7

At the time of writing this answer, there is an RFC for built-in signals.

I have had some success using the chan-signal crate:

#[macro_use]
extern crate chan;
extern crate chan_signal;

use chan_signal::Signal;

fn main() {
    // Signal gets a value when the OS sent a INT or TERM signal.
    let signal = chan_signal::notify(&[Signal::INT, Signal::TERM]);
    // When our work is complete, send a sentinel value on `sdone`.
    let (sdone, rdone) = chan::sync(0);
    // Run work.
    ::std::thread::spawn(move || run(sdone));

    // Wait for a signal or for work to be done.
    chan_select! {
        signal.recv() -> signal => {
            println!("received signal: {:?}", signal)
        },
        rdone.recv() => {
            println!("Program completed normally.");
        }
    }
}

fn run(_sdone: chan::Sender<()>) {
    println!("Running work for 5 seconds.");
    println!("Can you send a signal quickly enough?");
    // Do some work.
    ::std::thread::sleep_ms(5000);

    // _sdone gets dropped which closes the channel and causes `rdone`
    // to unblock.
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Alberto Leal
  • 520
  • 2
  • 10
  • 19
  • 1
    While this answer was valid at the time of posting, the [chan-signal](https://lib.rs/crates/chan-signal) crate did reach its end-of-life a couple of years ago and is considered deprecated by its author(s). – sampi Sep 29 '21 at 09:18
  • The core of the answer is still valid, though. 1) unload the work to a 3rd party code, 2) use approach that minimizes work done in the signal handler itself. – user7610 Feb 03 '22 at 21:23