single-user service manager
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181use crate::unit::service;use crate::{condition, ecs};use bevy_ecs::prelude::*;use calloop::generic;use rustix::pipe;use std::os::fd;use std::os::fd::FromRawFd;use std::os::linux::net::SocketAddrExt;use std::os::unix::net;use std::os::unix::process::CommandExt;use std::{io, mem, process};
pub trait Notify { fn configure_command( &mut self, loop_handle: &ecs::LoopHandle, entity: Entity, cmd: &mut process::Command, ) -> anyhow::Result<Option<calloop::RegistrationToken>>;}
#[derive(Resource)]pub struct Systemd { pid: u32, next_id: u32,}
impl Default for Systemd { fn default() -> Self { Self { pid: process::id(), next_id: 1, } }}
impl Notify for Systemd { fn configure_command( &mut self, loop_handle: &ecs::LoopHandle, entity: Entity, cmd: &mut process::Command, ) -> anyhow::Result<Option<calloop::RegistrationToken>> { let name = format!("run/apogee/notify/{}/{}", self.pid, self.next_id); self.next_id += 1;
let address = net::SocketAddr::from_abstract_name(name.as_bytes())?; let socket = net::UnixDatagram::bind_addr(&address)?; socket.set_nonblocking(true)?;
cmd.env("NOTIFY_SOCKET", format!("@{name}"));
let fd_wrapper = unsafe { generic::FdWrapper::new(socket) }; let source = generic::Generic::new( fd_wrapper, calloop::Interest { readable: true, writable: false, }, calloop::Mode::Level, ); let token = loop_handle .0 .insert_source(source, move |readiness, socket, state| { if !readiness.readable { return Ok(calloop::PostAction::Continue); }
let mut buffer = [0_u8; 8192];
loop { match socket.recv(&mut buffer) { Ok(length) => { let ready = buffer[..length] .split(|byte| *byte == b'\n') .any(|field| field == b"READY=1");
if !ready { continue; }
let Some(ready) = state.world.get::<service::Ready>(entity) else { return Ok(calloop::PostAction::Remove); };
state.world.entity_mut(ready.0).insert(condition::Fulfilled);
return Ok(calloop::PostAction::Remove); }
Err(error) if error.kind() == io::ErrorKind::WouldBlock => { return Ok(calloop::PostAction::Continue); }
Err(error) => return Err(error), } } })?;
Ok(Some(token)) }}
pub struct Dummy;
impl Notify for Dummy { fn configure_command( &mut self, loop_handle: &ecs::LoopHandle, entity: Entity, cmd: &mut process::Command, ) -> anyhow::Result<Option<calloop::RegistrationToken>> { _ = loop_handle; _ = entity; _ = cmd; Ok(None) }}
#[derive(Resource)]pub struct S6;
impl Notify for S6 { fn configure_command( &mut self, loop_handle: &ecs::LoopHandle, entity: Entity, cmd: &mut process::Command, ) -> anyhow::Result<Option<calloop::RegistrationToken>> { let (notify_read, notify_write) = pipe::pipe_with(pipe::PipeFlags::CLOEXEC)?;
unsafe { cmd.pre_exec(move || { let mut target = fd::OwnedFd::from_raw_fd(3); rustix::io::dup2(¬ify_write, &mut target).map_err(io::Error::from)?;
mem::forget(target);
Ok(()) }); }
let fd_wrapper = unsafe { generic::FdWrapper::new(notify_read) }; let source = generic::Generic::new( fd_wrapper, calloop::Interest { readable: true, writable: false, }, calloop::Mode::Level, ); let token = loop_handle.0.insert_source(source, move |_, fd, state| { let mut buf = [0u8; 64];
match rustix::io::read(fd, &mut buf) { Ok(0) => Ok(calloop::PostAction::Remove), Ok(n) => { if buf[..n].contains(&b'\n') { let Some(ready) = state.world.get::<service::Ready>(entity) else { return Ok(calloop::PostAction::Remove); };
state.world.entity_mut(ready.0).insert(condition::Fulfilled);
Ok(calloop::PostAction::Remove) } else { Ok(calloop::PostAction::Continue) } } Err(rustix::io::Errno::AGAIN) => Ok(calloop::PostAction::Continue), Err(err) => { log::error!("failed to read notify fd: {err}"); Ok(calloop::PostAction::Remove) } } })?;
Ok(Some(token)) }}