use std::error::Error;
use std::fs::{self, File, OpenOptions};
use std::io::{self, ErrorKind, Read, Seek, Write};
use std::os::unix::prelude::{AsRawFd, FromRawFd, IntoRawFd};
use std::result::Result;
use libsystemd::activation;
use libsystemd::daemon::{self, NotifyState};
fn create_and_store_persistent_state() -> Result<File, Box<dyn Error>> {
let path = format!("/dev/shm/persistent_state-{}", std::process::id());
let mut f = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.truncate(true)
.open(&path)?;
fs::remove_file(&path)?;
let nss = [
NotifyState::Fdname("persistent-state".to_owned()),
NotifyState::Fdstore,
];
daemon::notify_with_fds(false, &nss, &[f.as_raw_fd()])?;
let state = [0u8, 1];
f.set_len(state.len() as u64)?;
f.write_all(&state)?;
f.rewind()?;
Ok(f)
}
fn run() -> Result<i32, Box<dyn Error>> {
if !daemon::booted() {
println!("Not running systemd, early exit.");
return Ok(1);
};
let mut descriptors =
activation::receive_descriptors_with_names(false).unwrap_or_else(|_| Vec::new());
let mut persistent_state = if let Some((fd, name)) = descriptors.pop() {
println!("Fetched persistent state from systemd");
if name == "persistent-state" {
unsafe { File::from_raw_fd(fd.into_raw_fd()) }
} else {
let err = io::Error::new(ErrorKind::Other, "Got the wrong file descriptor.");
return Err(Box::new(err));
}
} else {
println!("Got nothing from systemd, create new persistent state");
create_and_store_persistent_state()?
};
let mut buf = [0xEFu8; 1];
persistent_state.read_exact(&mut buf)?;
persistent_state.rewind()?;
buf[0] += 1;
persistent_state.write_all(&buf)?;
persistent_state.rewind()?;
if buf[0] < 3 {
println!("State was: {}, exit and restart", buf[0]);
return Ok(2);
}
println!(
"Exiting normally because persistent state is now {}",
buf[0]
);
let nss = [
NotifyState::Fdname("persistent-state".to_owned()),
NotifyState::FdstoreRemove,
];
daemon::notify(false, &nss)?;
Ok(0)
}
pub fn main() -> Result<(), Box<dyn Error>> {
std::process::exit(run()?);
}