//! apollos-dbus: Apollo's Crib on the session bus.
//!
//! Connects to any number of Ankyrae and Keryxiae and keeps what they say as
//! objects on the session bus, for Plasma widgets and anything else to bind.
//! See README.md for the interface.
mod bus;
mod config;
mod points;
mod source;
mod store;
mod wire;
use std::collections::HashMap;
use std::path::PathBuf;
use std::time::Duration;
use anyhow::{Context, Result, bail};
use clap::Parser;
use tokio::sync::mpsc;
use tracing::info;
use crate::bus::Root;
use crate::config::{Config, Kind};
use crate::store::State;
#[derive(Parser)]
#[command(version, about)]
struct Args {
/// The config file. See README.md for what goes in it.
#[arg(short, long, env = "APOLLOS_DBUS_CONFIG")]
config: Option<PathBuf>,
}
#[tokio::main]
async fn main() -> Result<()> {
tracing_subscriber::fmt()
.with_env_filter(
tracing_subscriber::EnvFilter::try_from_default_env()
.unwrap_or_else(|_| "apollos_dbus=info".into()),
)
.init();
let args = Args::parse();
let path = args.config.unwrap_or_else(config::default_path);
let config = Config::load(&path)?;
let sources = config.sources()?;
if sources.is_empty() {
bail!(
"{} names no [[ankyra]] or [[keryx]]; nothing to do",
path.display()
);
}
let client_id = config.client_id.clone().unwrap_or_else(|| {
format!(
"apollos-dbus-{}",
gethostname::gethostname().to_string_lossy()
)
});
let (events_tx, events_rx) = mpsc::channel(64);
let mut commands = HashMap::new();
let mut runners = vec![];
for s in &sources {
let (tx, rx) = mpsc::channel(8);
commands.insert(s.label(), (matches!(s.kind, Kind::Keryx { .. }), tx));
// One client id per connection: the broker closes the older of two
// that share one. The first keeps the bare id, which is the one an
// Ankyra's allow-list is most likely to have been given.
let id = if runners.is_empty() {
client_id.clone()
} else {
format!("{client_id}-{}", s.name)
};
runners.push((s.clone(), id, rx));
}
let mut state = State::new(
sources.iter().map(|s| s.label()),
Duration::from_secs(config.stale_after_secs),
);
let cache = store::cache_path();
state.load(&cache);
let root = Root {
board: "{}".into(),
points: "[]".into(),
queries: vec![],
sources: "[]".into(),
connected: false,
origins: HashMap::new(),
commands,
};
let conn = zbus::connection::Builder::session()?
.serve_at(bus::ROOT, root)?
.serve_at(bus::ROOT, zbus::fdo::ObjectManager)?
.build()
.await?;
// Named only once the objects are there, so activation never hands a
// caller a name with nothing behind it.
conn.request_name(bus::NAME)
.await
.with_context(|| format!("claiming {} -- is another apollos-dbus running?", bus::NAME))?;
info!("on the session bus as {}", bus::NAME);
for (s, id, rx) in runners {
tokio::spawn(source::run(s, id, events_tx.clone(), rx));
}
drop(events_tx);
tokio::select! {
_ = store::run(conn, state, events_rx, cache) => {}
_ = tokio::signal::ctrl_c() => info!("interrupted"),
}
Ok(())
}
neiam
/apollos-dbus
public · Issues · Pulls · Labels · Forks · Compare · Actions success · Badges · Packages
⭐
Log in to mark this repository.
3.4 KiB · text
History
5fae34f