-
Notifications
You must be signed in to change notification settings - Fork 0
/
alarm.rs
117 lines (100 loc) · 3.94 KB
/
alarm.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
// MIT License
// Copyright (c) 2023 Michael de Gans
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
use clap::Parser;
use jeep::{
events::Event,
listener::{Error, Listener},
};
use std::{
os::unix::process::ExitStatusExt,
process::{Command, ExitCode, ExitStatus},
};
#[derive(Parser, Debug)]
#[command(
author,
version,
about = "Run a command when any doors are opened, then exit",
long_about = None
)]
struct Args {
/// Seconds to sleep before arming.
#[arg(long, default_value_t = 0)]
delay: u64,
/// CAN interface to open (eg. "can0").
#[arg(short, long)]
device: String,
/// Executable to run when any doors are opened.
#[arg(short, long)]
exe: String,
#[arg(short, long)]
/// Argments for the --exe to be run
args: Vec<String>,
}
/// Helper function to convert an ExitStatus to ExitCode
fn status_to_exitcode(status: ExitStatus) -> ExitCode {
match status.code() {
// The exit code is never be outside of u8 range on Unix.
Some(code) => (code as u8).into(),
// Process was terminated by some signal (eg, through top)
None => {
eprintln!(
"--exe terminated by signal: {}",
status.signal().unwrap()
);
ExitCode::FAILURE
}
}
}
/// Print countdown for `timeout` seconds, then "!!!ARMED!!!"
fn arm_delay(timeout: u64) {
for seconds_left in timeout..0 {
println!("ARMING_IN:{seconds_left}");
std::thread::sleep(std::time::Duration::from_secs(1))
}
println!("!!!ARMED!!!");
}
fn main() -> Result<ExitCode, Box<dyn std::error::Error>> {
let args = Args::parse();
// We use listener in blocking mode, which will block until there are more
// messages instead of returning when messages() are exhausted.
let listener = Listener::connect(&args.device, true)?;
// Do any delay before arming.
arm_delay(args.delay);
// Listen for messages on the CAN bus (blocking)
for message in listener.messages() {
match message {
// We only care about door messages
Ok(Event::Doors(doors)) => {
if doors.any_open() {
// launch the command and block until completion
let status =
Command::new(args.exe).args(args.args).status()?;
// convert the status code to an error code and exit
return Ok(status_to_exitcode(status));
}
}
// IO Error, so we probably want to exit, although in the future we
// could retry or notify seom server of the error.
Err(Error::IoError(e)) => return Err(Box::new(e)),
// ignore anything else (eg. ParseError, other events)
_ => continue,
}
}
// We should never actually reach here when the Listener is blocking.
panic!("`Messages` iterator broken -- yielded no messages")
}