--- /dev/null
+/target
+config.ron
+Cargo.lock
+deploy-to-ek.nu
--- /dev/null
+[package]
+name = "rpi_keep_alive"
+version = "0.1.0"
+authors = ["Greg Burri <greg.burri@gmail.com>"]
+edition = "2021"
+
+[dependencies]
+rand = "0.8"
+
+serde = { version = "1.0", features = ["derive"] }
+serde_json = "1.0"
+ron = "0.8"
+
+ping = "0.5"
+
+anyhow = "1"
+
+[profile.release]
+codegen-units = 1
+lto = true
+panic = 'abort'
--- /dev/null
+# 'zigbuild' is needed to build for target 'x86_64-unknown-linux-gnu' on linux:
+# https://github.com/rust-cross/cargo-zigbuild
+
+def main [host: string, destination: string, ssh_key: path] {
+ let ssh_args = [-i $ssh_key $host]
+ let scp_args = [-r -i $ssh_key]
+ let target = "x86_64-unknown-linux-gnu"
+ let app_name = "rpi_keep_alive"
+ let build = "release" # "debug" or "release".
+
+ def invoke_ssh [command: string] {
+ let args = $ssh_args ++ $command
+ print $"Executing: ssh ($args)"
+ ssh ...$args
+ }
+
+ def copy_ssh [source: string, destination: string] {
+ let args = $scp_args ++ [$source $"($host):($destination)"]
+ print $"Executing: scp ($args)"
+ scp ...$args
+ }
+
+ # Don't know how to dynamically pass variable arguments.
+ if $build == "release" {
+ cargo zigbuild --target $target --release
+ } else {
+ cargo zigbuild --target $target
+ }
+
+ # invoke_ssh $"systemctl stop ($app_name)"
+ copy_ssh ./target/($target)/($build)/($app_name) $destination
+ invoke_ssh $"chmod u+x ($destination)/($app_name)"
+ # invoke_ssh $"systemctl start ($app_name)"
+ print "Deployment finished"
+}
\ No newline at end of file
--- /dev/null
+use std::{fs::File, time::Duration};
+
+use anyhow::Result;
+use ron::{
+ de::from_reader,
+ ser::{to_writer_pretty, PrettyConfig},
+};
+use serde::{Deserialize, Serialize};
+
+#[derive(Debug, Clone, Deserialize, Serialize)]
+pub struct Config {
+ pub address: String,
+ pub duration: Duration,
+}
+
+impl Config {
+ pub fn default() -> Self {
+ Config {
+ address: "192.168.2.1".to_string(),
+ duration: Duration::from_secs(60),
+ }
+ }
+
+ pub fn read(file_path: &str) -> Result<Config> {
+ match File::open(file_path) {
+ Ok(file) => from_reader(file).map_err(|e| e.into()),
+ // The file doesn't exit -> create it with default values.
+ Err(_) => {
+ let file = File::create(file_path)?;
+ let default_config = Config::default();
+ to_writer_pretty(file, &default_config, PrettyConfig::new())?;
+ Ok(default_config)
+ }
+ }
+ }
+}
--- /dev/null
+use std::{
+ fmt,
+ net::{IpAddr, Ipv4Addr, UdpSocket},
+ str::FromStr,
+ thread,
+ time::{self, Duration},
+};
+
+use anyhow::Result;
+use ping::rawsock::ping;
+
+use crate::config::Config;
+
+mod config;
+
+const FILE_CONF: &str = "config.ron";
+const PING_TIMEOUT: Duration = Duration::from_secs(5);
+
+fn main() -> Result<()> {
+ println!("Raspberry Keep Alive");
+
+ let config = Config::read(FILE_CONF)?;
+ let ip_addr = IpAddr::V4(Ipv4Addr::from_str(&config.address)?);
+
+ println!("Configuration: {:?}", config);
+
+ loop {
+ let time_beginning_loop = time::Instant::now();
+
+ println!("Sending a ping to {}", config.address);
+
+ if let Err(error) = ping(ip_addr, Some(PING_TIMEOUT), None, None, None, None) {
+ println!("Error during ping: {:?}", error);
+ }
+
+ // println!("Time: {:?}", time::Instant::now() - time_beginning_loop);
+
+ let elapsed = time::Instant::now() - time_beginning_loop;
+
+ if elapsed < config.duration {
+ let to_wait = config.duration - elapsed;
+ thread::sleep(to_wait);
+ }
+ }
+}