Implemented signal handling.

This commit is contained in:
shy 2021-04-05 17:27:24 +02:00
parent bde5bc9307
commit b27667d5e2
3 changed files with 76 additions and 11 deletions

25
Cargo.lock generated
View file

@ -10,15 +10,15 @@ checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
name = "kt"
version = "0.1.0"
dependencies = [
"libc",
"signal-hook",
"termion",
]
[[package]]
name = "libc"
version = "0.2.91"
version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8916b1f6ca17130ec6568feccee27c156ad12037880833a3b842a823236502e7"
checksum = "56d855069fafbb9b344c0f962150cd2c1187975cb1c22c1522c240d8c4986714"
[[package]]
name = "numtoa"
@ -44,6 +44,25 @@ dependencies = [
"redox_syscall",
]
[[package]]
name = "signal-hook"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef33d6d0cd06e0840fba9985aab098c147e67e05cee14d412d3345ed14ff30ac"
dependencies = [
"libc",
"signal-hook-registry",
]
[[package]]
name = "signal-hook-registry"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "16f1d0fef1604ba8f7a073c7e701f213e056707210e9020af4528e0101ce11a6"
dependencies = [
"libc",
]
[[package]]
name = "termion"
version = "1.5.6"

View file

@ -7,5 +7,5 @@ edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
termion = "1.5"
libc = "0.2"
termion = "1.5.6"
signal-hook = "0.3.8"

View file

@ -1,5 +1,5 @@
extern crate termion;
extern crate libc;
extern crate signal_hook;
mod alarm;
mod clock;
mod common;
@ -7,6 +7,9 @@ mod layout;
use std::{time, thread, env};
use std::io::Write;
use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};
use signal_hook::flag;
use termion::{clear, color, cursor, style};
use termion::raw::{IntoRawMode, RawTerminal};
use termion::event::Key;
@ -24,11 +27,20 @@ const USAGE: &str =
-e, --exec [COMMAND] Execute \"COMMAND\" on alarm. Must be the last flag on
the command line. Everything after it is passed as
argument to \"COMMAND\". Every \"%s\" will be replaced
with the elapsed time in [(HH:)MM:SS] format.";
with the elapsed time in [(HH:)MM:SS] format.
SIGNALS: <SIGUSR1> Reset clock.
<SIGUSR2> Pause or un-pause clock.";
const MENUBAR: &str =
"[0-9] Add alarm [d] Delete alarm [SPACE] Pause [r] Reset [c] Clear color [q] Quit";
const MENUBAR_SHORT: &str =
"[0-9] Add [d] Delete [SPACE] Pause [r] Reset [c] Clear [q] Quit";
// Needed for signal_hook.
const SIGTSTP: usize = signal_hook::consts::SIGTSTP as usize;
const SIGTERM: usize = signal_hook::consts::SIGTERM as usize;
const SIGINT: usize = signal_hook::consts::SIGINT as usize;
const SIGUSR1: usize = signal_hook::consts::SIGUSR1 as usize;
const SIGUSR2: usize = signal_hook::consts::SIGUSR2 as usize;
pub struct Config {
plain: bool,
@ -56,6 +68,10 @@ fn main() {
let mut buffer_updated: bool = false;
let mut countdown = Countdown::new();
// Register signal handlers.
let signal = Arc::new(AtomicUsize::new(0));
register_signal_handlers(&signal);
// Clear screen and hide cursor.
write!(stdout,
"{}{}",
@ -67,6 +83,29 @@ fn main() {
});
loop {
// Process received signals.
match signal.swap(0, Ordering::Relaxed) {
// No signal received.
0 => (),
// Suspend execution on SIGTSTP.
SIGTSTP => {
suspend(&mut stdout);
layout.force_redraw = true;
},
// Exit main loop on SIGTERM and SIGINT.
SIGTERM | SIGINT => break,
// Reset clock on SIGUSR1.
SIGUSR1 => {
clock.reset();
alarm_roster.reset_all();
layout.force_redraw = true;
},
// (Un-)Pause clock on SIGUSR2.
SIGUSR2 => clock.toggle(),
// We didn't register anything else.
_ => unreachable!(),
}
// Process input.
if let Some(key) = input_keys.next() {
match key.expect("Error reading input") {
@ -269,7 +308,15 @@ fn parse_args(config: &mut Config) {
}
}
// Suspend execution by raising SIGTSTP.
fn register_signal_handlers(signal: &Arc<AtomicUsize>) {
flag::register_usize(SIGTSTP as i32, Arc::clone(&signal), SIGTSTP).unwrap();
flag::register_usize(SIGTERM as i32, Arc::clone(&signal), SIGTERM).unwrap();
flag::register_usize(SIGINT as i32, Arc::clone(&signal), SIGINT).unwrap();
flag::register_usize(SIGUSR1 as i32, Arc::clone(&signal), SIGUSR1).unwrap();
flag::register_usize(SIGUSR2 as i32, Arc::clone(&signal), SIGUSR2).unwrap();
}
// Suspend execution on SIGTSTP.
fn suspend<W: Write>(stdout: &mut RawTerminal<W>) {
write!(stdout,
"{}{}{}",
@ -283,9 +330,8 @@ fn suspend<W: Write>(stdout: &mut RawTerminal<W>) {
eprintln!("Failed to leave raw terminal mode prior to suspend: {}", error);
});
let result = unsafe { libc::raise(libc::SIGTSTP) };
if result != 0 {
panic!("{}", std::io::Error::last_os_error());
if let Err(error) = signal_hook::low_level::emulate_default_handler(SIGTSTP as i32) {
eprintln!("Error raising SIGTSTP: {}", error);
}
stdout.activate_raw_mode()