Skip to content

Commit

Permalink
[Internals] More improvements to reboot-series milestones
Browse files Browse the repository at this point in the history
  • Loading branch information
sisungo committed Dec 10, 2023
1 parent 6f8296c commit de6f6c1
Show file tree
Hide file tree
Showing 6 changed files with 96 additions and 9 deletions.
16 changes: 16 additions & 0 deletions airup-sdk/src/system.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,22 @@ pub enum Status {
Stopped,
}

/// Item of an log record
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LogRecord {
pub timestamp: i64,
pub module: String,
pub message: String,
}

/// Information of an entered milestone.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EnteredMilestone {
pub name: String,
pub begin_timestamp: i64,
pub finish_timestamp: i64,
}

pub trait ConnectionExt {
/// Sideloads a service.
fn sideload_service(
Expand Down Expand Up @@ -132,6 +141,9 @@ pub trait ConnectionExt {
subject: &str,
n: usize,
) -> impl Future<Output = anyhow::Result<Result<Vec<LogRecord>, Error>>>;

/// Enters the specific milestone.
fn enter_milestone(&mut self, name: &str) -> impl Future<Output = anyhow::Result<Result<(), Error>>>;
}
impl ConnectionExt for super::Connection {
async fn sideload_service(
Expand Down Expand Up @@ -206,4 +218,8 @@ impl ConnectionExt for super::Connection {
) -> anyhow::Result<Result<Vec<LogRecord>, Error>> {
self.invoke("system.tail_logs", (subject, n)).await
}

async fn enter_milestone(&mut self, name: &str) -> anyhow::Result<Result<(), Error>> {
self.invoke("system.enter_milestone", name).await
}
}
1 change: 0 additions & 1 deletion airup/src/edit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ pub async fn main(cmdline: Cmdline) -> anyhow::Result<()> {
} else {
Err(anyhow!("file suffix must be specified to edit"))
}

}
}

Expand Down
6 changes: 5 additions & 1 deletion airupd/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,14 @@ impl Airupd {

/// Queries information about the whole system.
pub async fn query_system(&self) -> QuerySystem {
let booted_since = self
.query_milestone_stack()
.last()
.map(|x| x.finish_timestamp);
QuerySystem {
status: Status::Active,
boot_timestamp: self.boot_timestamp,
booted_since: self.booted_since(),
booted_since,
is_booting: self.is_booting(),
hostname: airupfx::env::host_name(),
services: self.supervisors.list().await,
Expand Down
9 changes: 9 additions & 0 deletions airupd/src/ipc/api/system.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ pub fn init<H: BuildHasher>(methods: &mut HashMap<&'static str, Method, H>) {
methods.insert("system.list_services", list_services);
methods.insert("system.use_logger", use_logger);
methods.insert("system.tail_logs", tail_logs);
methods.insert("system.enter_milestone", enter_milestone);
methods.insert("system.poweroff", poweroff);
methods.insert("system.reboot", reboot);
methods.insert("system.halt", halt);
Expand Down Expand Up @@ -153,6 +154,14 @@ fn tail_logs(_: Arc<SessionContext>, req: Request) -> MethodFuture {
})
}

fn enter_milestone(_: Arc<SessionContext>, req: Request) -> MethodFuture {
Box::pin(async move {
let name: String = req.extract_params()?;
airupd().enter_milestone(name).await?;
ok_null()
})
}

fn poweroff(_: Arc<SessionContext>, _: Request) -> MethodFuture {
Box::pin(async move {
airupd().lifetime.poweroff();
Expand Down
24 changes: 17 additions & 7 deletions airupd/src/milestones/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@
pub mod early_boot;
mod reboot;

use crate::app;
use crate::app::{self, airupd};
use ahash::AHashSet;
use airup_sdk::{
files::{
milestone::{Item, Kind},
Milestone,
},
system::EnteredMilestone,
Error,
};
use airupfx::prelude::*;
Expand All @@ -22,7 +23,7 @@ use std::sync::{
#[derive(Debug, Default)]
pub struct Manager {
is_booting: AtomicBool,
booted_since: RwLock<Option<i64>>,
stack: RwLock<Vec<EnteredMilestone>>,
}
impl Manager {
/// Creates a new [`Manager`] instance.
Expand Down Expand Up @@ -51,7 +52,6 @@ impl crate::app::Airupd {

self.enter_milestone(name).await.ok();

*self.milestones.booted_since.write().unwrap() = Some(airupfx::time::timestamp_ms());
self.milestones
.is_booting
.store(false, atomic::Ordering::Relaxed);
Expand All @@ -63,9 +63,9 @@ impl crate::app::Airupd {
self.milestones.is_booting.load(atomic::Ordering::Relaxed)
}

/// Returns a timestamp of boot completion.
pub fn booted_since(&self) -> Option<i64> {
*self.milestones.booted_since.read().unwrap()
/// Queries the milestone stack.
pub fn query_milestone_stack(&self) -> Vec<EnteredMilestone> {
self.milestones.stack.read().unwrap().clone()
}
}

Expand All @@ -91,6 +91,7 @@ fn enter_milestone(name: String, hist: &mut AHashSet<String>) -> BoxFuture<'_, R
}

tracing::info!(target: "console", "Entering milestone {}", def.display_name());
let begin_timestamp = airupfx::time::timestamp_ms();

// Enters dependency milestones
for dep in def.manifest.milestone.dependencies.iter() {
Expand All @@ -104,6 +105,15 @@ fn enter_milestone(name: String, hist: &mut AHashSet<String>) -> BoxFuture<'_, R
// Starts services
exec_milestone(&def).await;

// Record the milestone as entered
let finish_timestamp = airupfx::time::timestamp_ms();
let record = EnteredMilestone {
name: name.clone(),
begin_timestamp,
finish_timestamp,
};
airupd().milestones.stack.write().unwrap().push(record);

Ok(())
})
}
Expand Down Expand Up @@ -222,7 +232,7 @@ async fn display_name(name: &str) -> String {
.query_service(name)
.await
.map(|x| x.definition.display_name().into())
.unwrap_or_else(|_| name.into())
.unwrap_or_else(|_| format!("`{name}`"))
}

pub async fn run_wait(ace: &Ace, cmd: &str) -> anyhow::Result<()> {
Expand Down
49 changes: 49 additions & 0 deletions airupd/src/milestones/reboot.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
//! The `reboot` milestone preset series.
use crate::app::airupd;
use ahash::AHashSet;
use airup_sdk::Error;
use tokio::task::JoinHandle;
use std::time::Duration;

pub const PRESETS: &[&str] = &["reboot", "poweroff", "halt", "ctrlaltdel"];

Expand All @@ -24,6 +27,9 @@ async fn enter_reboot() -> Result<(), Error> {
super::enter_milestone("reboot".into(), &mut AHashSet::with_capacity(8))
.await
.ok();

stop_all_services(Duration::from_millis(5000)).await;

Ok(())
}

Expand All @@ -32,6 +38,9 @@ async fn enter_poweroff() -> Result<(), Error> {
super::enter_milestone("poweroff".into(), &mut AHashSet::with_capacity(8))
.await
.ok();

stop_all_services(Duration::from_millis(5000)).await;

Ok(())
}

Expand All @@ -40,6 +49,9 @@ async fn enter_halt() -> Result<(), Error> {
super::enter_milestone("halt".into(), &mut AHashSet::with_capacity(8))
.await
.ok();

stop_all_services(Duration::from_millis(5000)).await;

Ok(())
}

Expand All @@ -55,3 +67,40 @@ async fn enter_ctrlaltdel() -> Result<(), Error> {
Err(_) => enter_reboot().await,
}
}

/// Stops all running services.
async fn stop_all_services(timeout: Duration) {
tokio::time::timeout(timeout, async {
let services = airupd().supervisors.list().await;
let mut join_handles = Vec::with_capacity(services.len());
for service in services {
join_handles.push(stop_service_task(service));
}
for join_handle in join_handles {
join_handle.await.ok();
}
})
.await
.ok();
}

/// Spawns a task to interactively stop a service.
fn stop_service_task(service: String) -> JoinHandle<()> {
tokio::spawn(async move {
match airupd().stop_service(&service).await {
Ok(_) => {
tracing::info!("Stopping {}", super::display_name(&service).await);
}
Err(err) => {
if matches!(err, Error::UnitNotFound | Error::UnitNotStarted) {
return;
}
tracing::error!(
"Failed to stop {}: {}",
super::display_name(&service).await,
err
);
}
};
})
}

0 comments on commit de6f6c1

Please sign in to comment.