Add D-Bus session and system services
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
@@ -0,0 +1,218 @@
|
||||
use std::{collections::{BTreeMap, HashMap}, sync::Arc};
|
||||
|
||||
use zbus::{
|
||||
interface,
|
||||
object_server::SignalEmitter,
|
||||
zvariant::{OwnedObjectPath, Value},
|
||||
};
|
||||
|
||||
use crate::inventory::{BlockDevice, DriveDevice, Inventory};
|
||||
|
||||
type PropertyMap = BTreeMap<String, Value<'static>>;
|
||||
type InterfaceMap = BTreeMap<String, PropertyMap>;
|
||||
pub type ManagedObjects = HashMap<OwnedObjectPath, InterfaceMap>;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ObjectManagerRoot {
|
||||
inventory: Arc<Inventory>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct UDisksManager {
|
||||
inventory: Arc<Inventory>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct BlockDeviceInterface {
|
||||
block: BlockDevice,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct DriveInterface {
|
||||
drive: DriveDevice,
|
||||
}
|
||||
|
||||
impl ObjectManagerRoot {
|
||||
pub fn new(inventory: Arc<Inventory>) -> Self {
|
||||
Self { inventory }
|
||||
}
|
||||
}
|
||||
|
||||
impl UDisksManager {
|
||||
pub fn new(inventory: Arc<Inventory>) -> Self {
|
||||
Self { inventory }
|
||||
}
|
||||
}
|
||||
|
||||
impl BlockDeviceInterface {
|
||||
pub fn new(block: BlockDevice) -> Self {
|
||||
Self { block }
|
||||
}
|
||||
}
|
||||
|
||||
impl DriveInterface {
|
||||
pub fn new(drive: DriveDevice) -> Self {
|
||||
Self { drive }
|
||||
}
|
||||
}
|
||||
|
||||
#[interface(name = "org.freedesktop.DBus.ObjectManager")]
|
||||
impl ObjectManagerRoot {
|
||||
fn get_managed_objects(&self) -> ManagedObjects {
|
||||
let mut objects = HashMap::new();
|
||||
|
||||
objects.insert(self.inventory.manager_path(), manager_interfaces());
|
||||
|
||||
for drive in self.inventory.drives() {
|
||||
objects.insert(drive.object_path.clone(), drive_interfaces(drive));
|
||||
}
|
||||
|
||||
for block in self.inventory.blocks() {
|
||||
objects.insert(block.object_path.clone(), block_interfaces(block));
|
||||
}
|
||||
|
||||
objects
|
||||
}
|
||||
|
||||
#[zbus(signal, name = "InterfacesAdded")]
|
||||
async fn interfaces_added(
|
||||
signal_emitter: &SignalEmitter<'_>,
|
||||
object_path: OwnedObjectPath,
|
||||
interfaces_and_properties: InterfaceMap,
|
||||
) -> zbus::Result<()>;
|
||||
|
||||
#[zbus(signal, name = "InterfacesRemoved")]
|
||||
async fn interfaces_removed(
|
||||
signal_emitter: &SignalEmitter<'_>,
|
||||
object_path: OwnedObjectPath,
|
||||
interfaces: Vec<String>,
|
||||
) -> zbus::Result<()>;
|
||||
}
|
||||
|
||||
#[interface(name = "org.freedesktop.UDisks2.Manager")]
|
||||
impl UDisksManager {
|
||||
fn get_block_devices(&self, _options: HashMap<String, Value<'_>>) -> Vec<OwnedObjectPath> {
|
||||
self.inventory.block_paths()
|
||||
}
|
||||
|
||||
fn get_drives(&self, _options: HashMap<String, Value<'_>>) -> Vec<OwnedObjectPath> {
|
||||
self.inventory.drive_paths()
|
||||
}
|
||||
|
||||
#[zbus(property(emits_changed_signal = "const"), name = "Version")]
|
||||
fn version(&self) -> String {
|
||||
env!("CARGO_PKG_VERSION").to_string()
|
||||
}
|
||||
|
||||
#[zbus(property(emits_changed_signal = "const"), name = "SupportedFilesystems")]
|
||||
fn supported_filesystems(&self) -> Vec<String> {
|
||||
Vec::new()
|
||||
}
|
||||
|
||||
#[zbus(property(emits_changed_signal = "const"), name = "SupportedEncryptionTypes")]
|
||||
fn supported_encryption_types(&self) -> Vec<String> {
|
||||
Vec::new()
|
||||
}
|
||||
|
||||
#[zbus(property(emits_changed_signal = "const"), name = "DefaultEncryptionType")]
|
||||
fn default_encryption_type(&self) -> String {
|
||||
String::new()
|
||||
}
|
||||
}
|
||||
|
||||
#[interface(name = "org.freedesktop.UDisks2.Block")]
|
||||
impl BlockDeviceInterface {
|
||||
#[zbus(property(emits_changed_signal = "const"), name = "Device")]
|
||||
fn device(&self) -> Vec<u8> {
|
||||
self.block.device_path.as_bytes().to_vec()
|
||||
}
|
||||
|
||||
#[zbus(property(emits_changed_signal = "const"), name = "PreferredDevice")]
|
||||
fn preferred_device(&self) -> Vec<u8> {
|
||||
self.block.device_path.as_bytes().to_vec()
|
||||
}
|
||||
|
||||
#[zbus(property(emits_changed_signal = "const"), name = "Symlinks")]
|
||||
fn symlinks(&self) -> Vec<Vec<u8>> {
|
||||
Vec::new()
|
||||
}
|
||||
|
||||
#[zbus(property(emits_changed_signal = "const"), name = "Size")]
|
||||
fn size(&self) -> u64 {
|
||||
self.block.size
|
||||
}
|
||||
|
||||
#[zbus(property(emits_changed_signal = "const"), name = "ReadOnly")]
|
||||
fn read_only(&self) -> bool {
|
||||
self.block.read_only
|
||||
}
|
||||
|
||||
#[zbus(property(emits_changed_signal = "const"), name = "Drive")]
|
||||
fn drive(&self) -> OwnedObjectPath {
|
||||
self.block.drive_object_path.clone()
|
||||
}
|
||||
|
||||
#[zbus(property(emits_changed_signal = "const"), name = "HintPartitionable")]
|
||||
fn hint_partitionable(&self) -> bool {
|
||||
self.block.hint_partitionable
|
||||
}
|
||||
}
|
||||
|
||||
#[interface(name = "org.freedesktop.UDisks2.Drive")]
|
||||
impl DriveInterface {
|
||||
#[zbus(property(emits_changed_signal = "const"), name = "ConnectionBus")]
|
||||
fn connection_bus(&self) -> String {
|
||||
self.drive.scheme_identity.clone()
|
||||
}
|
||||
|
||||
#[zbus(property(emits_changed_signal = "const"), name = "Size")]
|
||||
fn size(&self) -> u64 {
|
||||
self.drive.size
|
||||
}
|
||||
}
|
||||
|
||||
fn manager_interfaces() -> InterfaceMap {
|
||||
let mut properties = BTreeMap::new();
|
||||
properties.insert(String::from("Version"), Value::new(env!("CARGO_PKG_VERSION").to_string()));
|
||||
properties.insert(String::from("SupportedFilesystems"), Value::new(Vec::<String>::new()));
|
||||
properties.insert(
|
||||
String::from("SupportedEncryptionTypes"),
|
||||
Value::new(Vec::<String>::new()),
|
||||
);
|
||||
properties.insert(String::from("DefaultEncryptionType"), Value::new(String::new()));
|
||||
|
||||
BTreeMap::from([(String::from("org.freedesktop.UDisks2.Manager"), properties)])
|
||||
}
|
||||
|
||||
fn drive_interfaces(drive: &DriveDevice) -> InterfaceMap {
|
||||
let mut properties = BTreeMap::new();
|
||||
properties.insert(
|
||||
String::from("ConnectionBus"),
|
||||
Value::new(drive.scheme_identity.clone()),
|
||||
);
|
||||
properties.insert(String::from("Size"), Value::new(drive.size));
|
||||
|
||||
BTreeMap::from([(String::from("org.freedesktop.UDisks2.Drive"), properties)])
|
||||
}
|
||||
|
||||
fn block_interfaces(block: &BlockDevice) -> InterfaceMap {
|
||||
let mut properties = BTreeMap::new();
|
||||
properties.insert(String::from("Device"), Value::new(block.device_path.as_bytes().to_vec()));
|
||||
properties.insert(
|
||||
String::from("PreferredDevice"),
|
||||
Value::new(block.device_path.as_bytes().to_vec()),
|
||||
);
|
||||
properties.insert(String::from("Symlinks"), Value::new(Vec::<Vec<u8>>::new()));
|
||||
properties.insert(String::from("Size"), Value::new(block.size));
|
||||
properties.insert(String::from("ReadOnly"), Value::new(block.read_only));
|
||||
properties.insert(
|
||||
String::from("Drive"),
|
||||
Value::new(block.drive_object_path.clone()),
|
||||
);
|
||||
properties.insert(
|
||||
String::from("HintPartitionable"),
|
||||
Value::new(block.hint_partitionable),
|
||||
);
|
||||
|
||||
BTreeMap::from([(String::from("org.freedesktop.UDisks2.Block"), properties)])
|
||||
}
|
||||
@@ -0,0 +1,285 @@
|
||||
use std::{
|
||||
collections::BTreeMap,
|
||||
fs,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
#[cfg(unix)]
|
||||
use std::os::unix::fs::MetadataExt;
|
||||
|
||||
use zbus::zvariant::OwnedObjectPath;
|
||||
|
||||
pub const ROOT_PATH: &str = "/org/freedesktop/UDisks2";
|
||||
pub const MANAGER_PATH: &str = "/org/freedesktop/UDisks2/Manager";
|
||||
pub const BLOCK_DEVICES_PREFIX: &str = "/org/freedesktop/UDisks2/block_devices";
|
||||
pub const DRIVES_PREFIX: &str = "/org/freedesktop/UDisks2/drives";
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Inventory {
|
||||
manager_path: OwnedObjectPath,
|
||||
drives: Vec<DriveDevice>,
|
||||
blocks: Vec<BlockDevice>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct DriveDevice {
|
||||
pub object_path: OwnedObjectPath,
|
||||
pub scheme_identity: String,
|
||||
pub size: u64,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct BlockDevice {
|
||||
pub object_path: OwnedObjectPath,
|
||||
pub drive_object_path: OwnedObjectPath,
|
||||
pub device_path: String,
|
||||
pub size: u64,
|
||||
// UDisks2's base Drive/Block interfaces do not expose logical block size directly,
|
||||
// but Red Bear still derives and retains it from real file metadata.
|
||||
pub logical_block_size: u64,
|
||||
pub read_only: bool,
|
||||
pub hint_partitionable: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
|
||||
struct RootKey {
|
||||
disk_number: u32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
|
||||
struct PartitionKey {
|
||||
disk_number: u32,
|
||||
partition_number: u32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
enum EntryKind {
|
||||
Root(RootKey),
|
||||
Partition(PartitionKey),
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
struct DeviceMetadata {
|
||||
size: u64,
|
||||
logical_block_size: u64,
|
||||
read_only: bool,
|
||||
}
|
||||
|
||||
impl Inventory {
|
||||
pub fn scan() -> Self {
|
||||
let mut drives = Vec::new();
|
||||
let mut blocks = Vec::new();
|
||||
|
||||
for scheme_name in read_dir_names("/scheme")
|
||||
.unwrap_or_default()
|
||||
.into_iter()
|
||||
.filter(|name| name.starts_with("disk."))
|
||||
{
|
||||
let scheme_path = PathBuf::from("/scheme").join(&scheme_name);
|
||||
let scheme_identity = scheme_name
|
||||
.strip_prefix("disk.")
|
||||
.unwrap_or(&scheme_name)
|
||||
.to_string();
|
||||
let entries = read_dir_names(&scheme_path).unwrap_or_default();
|
||||
|
||||
let mut roots = BTreeMap::new();
|
||||
let mut partitions = Vec::new();
|
||||
|
||||
for entry_name in entries {
|
||||
match parse_entry_name(&entry_name) {
|
||||
Some(EntryKind::Root(root_key)) => {
|
||||
roots.insert(root_key, entry_name);
|
||||
}
|
||||
Some(EntryKind::Partition(partition_key)) => {
|
||||
partitions.push((partition_key, entry_name));
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
}
|
||||
|
||||
let mut drive_paths = BTreeMap::new();
|
||||
|
||||
for (root_key, entry_name) in roots {
|
||||
let device_path = format!("{}/{entry_name}", scheme_path.display());
|
||||
let Some(metadata) = read_device_metadata(Path::new(&device_path)) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
let drive = DriveDevice {
|
||||
object_path: owned_object_path(&format!(
|
||||
"{DRIVES_PREFIX}/{}",
|
||||
stable_object_name(&scheme_name, &entry_name)
|
||||
)),
|
||||
scheme_identity: scheme_identity.clone(),
|
||||
size: metadata.size,
|
||||
};
|
||||
|
||||
drive_paths.insert(root_key, drive.object_path.clone());
|
||||
|
||||
blocks.push(BlockDevice {
|
||||
object_path: owned_object_path(&format!(
|
||||
"{BLOCK_DEVICES_PREFIX}/{}",
|
||||
stable_object_name(&scheme_name, &entry_name)
|
||||
)),
|
||||
drive_object_path: drive.object_path.clone(),
|
||||
device_path,
|
||||
size: metadata.size,
|
||||
logical_block_size: metadata.logical_block_size,
|
||||
read_only: metadata.read_only,
|
||||
hint_partitionable: true,
|
||||
});
|
||||
|
||||
drives.push(drive);
|
||||
}
|
||||
|
||||
partitions.sort_by_key(|(partition_key, _)| *partition_key);
|
||||
for (partition_key, entry_name) in partitions {
|
||||
let Some(drive_object_path) = drive_paths.get(&RootKey {
|
||||
disk_number: partition_key.disk_number,
|
||||
}) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
let device_path = format!("{}/{entry_name}", scheme_path.display());
|
||||
let Some(metadata) = read_device_metadata(Path::new(&device_path)) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
blocks.push(BlockDevice {
|
||||
object_path: owned_object_path(&format!(
|
||||
"{BLOCK_DEVICES_PREFIX}/{}",
|
||||
stable_object_name(&scheme_name, &entry_name)
|
||||
)),
|
||||
drive_object_path: drive_object_path.clone(),
|
||||
device_path,
|
||||
size: metadata.size,
|
||||
logical_block_size: metadata.logical_block_size,
|
||||
read_only: metadata.read_only,
|
||||
hint_partitionable: false,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Self {
|
||||
manager_path: owned_object_path(MANAGER_PATH),
|
||||
drives,
|
||||
blocks,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn manager_path(&self) -> OwnedObjectPath {
|
||||
self.manager_path.clone()
|
||||
}
|
||||
|
||||
pub fn drives(&self) -> &[DriveDevice] {
|
||||
&self.drives
|
||||
}
|
||||
|
||||
pub fn blocks(&self) -> &[BlockDevice] {
|
||||
&self.blocks
|
||||
}
|
||||
|
||||
pub fn drive_paths(&self) -> Vec<OwnedObjectPath> {
|
||||
self.drives
|
||||
.iter()
|
||||
.map(|drive| drive.object_path.clone())
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn block_paths(&self) -> Vec<OwnedObjectPath> {
|
||||
self.blocks
|
||||
.iter()
|
||||
.map(|block| block.object_path.clone())
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
fn read_dir_names(path: impl AsRef<Path>) -> Option<Vec<String>> {
|
||||
let mut names = Vec::new();
|
||||
for entry in fs::read_dir(path).ok()? {
|
||||
let entry = entry.ok()?;
|
||||
let name = entry.file_name();
|
||||
let name = name.to_str()?.to_string();
|
||||
names.push(name);
|
||||
}
|
||||
names.sort();
|
||||
Some(names)
|
||||
}
|
||||
|
||||
fn parse_entry_name(entry_name: &str) -> Option<EntryKind> {
|
||||
if let Some(position) = entry_name.find('p') {
|
||||
let disk_number = entry_name[..position].parse().ok()?;
|
||||
let partition_number = entry_name[position + 1..].parse().ok()?;
|
||||
return Some(EntryKind::Partition(PartitionKey {
|
||||
disk_number,
|
||||
partition_number,
|
||||
}));
|
||||
}
|
||||
|
||||
Some(EntryKind::Root(RootKey {
|
||||
disk_number: entry_name.parse().ok()?,
|
||||
}))
|
||||
}
|
||||
|
||||
fn read_device_metadata(path: &Path) -> Option<DeviceMetadata> {
|
||||
let metadata = fs::metadata(path).ok()?;
|
||||
let logical_block_size = metadata_logical_block_size(&metadata);
|
||||
|
||||
Some(DeviceMetadata {
|
||||
size: metadata.len(),
|
||||
logical_block_size,
|
||||
read_only: metadata.permissions().readonly(),
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
fn metadata_logical_block_size(metadata: &fs::Metadata) -> u64 {
|
||||
metadata.blksize()
|
||||
}
|
||||
|
||||
#[cfg(not(unix))]
|
||||
fn metadata_logical_block_size(_metadata: &fs::Metadata) -> u64 {
|
||||
0
|
||||
}
|
||||
|
||||
fn stable_object_name(scheme_name: &str, entry_name: &str) -> String {
|
||||
format!(
|
||||
"{}_{}",
|
||||
encode_path_component(scheme_name),
|
||||
encode_path_component(entry_name)
|
||||
)
|
||||
}
|
||||
|
||||
fn encode_path_component(component: &str) -> String {
|
||||
let mut encoded = String::new();
|
||||
|
||||
for byte in component.bytes() {
|
||||
if byte.is_ascii_alphanumeric() {
|
||||
encoded.push(byte as char);
|
||||
} else {
|
||||
encoded.push('_');
|
||||
encoded.push(hex_char(byte >> 4));
|
||||
encoded.push(hex_char(byte & 0x0f));
|
||||
}
|
||||
}
|
||||
|
||||
if encoded.is_empty() {
|
||||
encoded.push('_');
|
||||
encoded.push('0');
|
||||
encoded.push('0');
|
||||
}
|
||||
|
||||
encoded
|
||||
}
|
||||
|
||||
fn hex_char(value: u8) -> char {
|
||||
match value {
|
||||
0..=9 => (b'0' + value) as char,
|
||||
10..=15 => (b'a' + (value - 10)) as char,
|
||||
_ => unreachable!("hex nibble out of range"),
|
||||
}
|
||||
}
|
||||
|
||||
fn owned_object_path(path: &str) -> OwnedObjectPath {
|
||||
OwnedObjectPath::try_from(path.to_string()).expect("generated object path must be valid")
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
mod interfaces;
|
||||
mod inventory;
|
||||
|
||||
use std::{
|
||||
env,
|
||||
error::Error,
|
||||
process,
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
use interfaces::{BlockDeviceInterface, DriveInterface, ObjectManagerRoot, UDisksManager};
|
||||
use inventory::{Inventory, MANAGER_PATH, ROOT_PATH};
|
||||
use tokio::runtime::Builder as RuntimeBuilder;
|
||||
use zbus::{
|
||||
Address,
|
||||
connection::Builder as ConnectionBuilder,
|
||||
zvariant::OwnedObjectPath,
|
||||
};
|
||||
|
||||
const BUS_NAME: &str = "org.freedesktop.UDisks2";
|
||||
|
||||
enum Command {
|
||||
Run,
|
||||
Help,
|
||||
}
|
||||
|
||||
fn usage() -> &'static str {
|
||||
"Usage: redbear-udisks [--help]"
|
||||
}
|
||||
|
||||
fn parse_args() -> Result<Command, String> {
|
||||
let mut args = env::args().skip(1);
|
||||
|
||||
match args.next() {
|
||||
None => Ok(Command::Run),
|
||||
Some(arg) if arg == "--help" || arg == "-h" => {
|
||||
if args.next().is_some() {
|
||||
return Err(String::from("unexpected extra arguments after --help"));
|
||||
}
|
||||
|
||||
Ok(Command::Help)
|
||||
}
|
||||
Some(arg) => Err(format!("unrecognized argument '{arg}'")),
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_object_path(path: &str) -> Result<OwnedObjectPath, Box<dyn Error>> {
|
||||
Ok(OwnedObjectPath::try_from(path.to_owned())?)
|
||||
}
|
||||
|
||||
fn system_connection_builder() -> Result<ConnectionBuilder<'static>, Box<dyn Error>> {
|
||||
if let Ok(address) = env::var("DBUS_STARTER_ADDRESS") {
|
||||
Ok(ConnectionBuilder::address(Address::try_from(address.as_str())?)?)
|
||||
} else {
|
||||
Ok(ConnectionBuilder::system()?)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(unix, not(target_os = "redox")))]
|
||||
async fn wait_for_shutdown() -> Result<(), Box<dyn Error>> {
|
||||
use tokio::signal::unix::{SignalKind, signal};
|
||||
|
||||
let mut terminate = signal(SignalKind::terminate())?;
|
||||
|
||||
tokio::select! {
|
||||
_ = terminate.recv() => Ok(()),
|
||||
_ = tokio::signal::ctrl_c() => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "redox")]
|
||||
async fn wait_for_shutdown() -> Result<(), Box<dyn Error>> {
|
||||
std::future::pending::<()>().await;
|
||||
#[allow(unreachable_code)]
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(all(not(unix), not(target_os = "redox")))]
|
||||
async fn wait_for_shutdown() -> Result<(), Box<dyn Error>> {
|
||||
tokio::signal::ctrl_c().await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn run_daemon() -> Result<(), Box<dyn Error>> {
|
||||
eprintln!("redbear-udisks: startup begin");
|
||||
let _root_path = parse_object_path(ROOT_PATH)?;
|
||||
let _manager_path = parse_object_path(MANAGER_PATH)?;
|
||||
eprintln!("redbear-udisks: object paths parsed");
|
||||
let inventory = Arc::new(Inventory::scan());
|
||||
eprintln!(
|
||||
"redbear-udisks: inventory scanned drives={} blocks={}",
|
||||
inventory.drives().len(),
|
||||
inventory.blocks().len()
|
||||
);
|
||||
let block_sizes_known = inventory
|
||||
.blocks()
|
||||
.iter()
|
||||
.filter(|block| block.logical_block_size > 0)
|
||||
.count();
|
||||
|
||||
eprintln!("redbear-udisks: starter address={:?}", env::var("DBUS_STARTER_ADDRESS").ok());
|
||||
eprintln!("redbear-udisks: building D-Bus connection");
|
||||
let mut builder = system_connection_builder()?
|
||||
.name(BUS_NAME)?
|
||||
.serve_at(ROOT_PATH, ObjectManagerRoot::new(inventory.clone()))?
|
||||
.serve_at(MANAGER_PATH, UDisksManager::new(inventory.clone()))?;
|
||||
|
||||
for drive in inventory.drives() {
|
||||
builder = builder.serve_at(drive.object_path.as_str(), DriveInterface::new(drive.clone()))?;
|
||||
}
|
||||
|
||||
for block in inventory.blocks() {
|
||||
builder = builder.serve_at(block.object_path.as_str(), BlockDeviceInterface::new(block.clone()))?;
|
||||
}
|
||||
|
||||
let connection = builder.build().await?;
|
||||
|
||||
eprintln!(
|
||||
"redbear-udisks: registered {BUS_NAME} on the system bus with {} drives, {} block devices, {} metadata-backed block sizes",
|
||||
inventory.drives().len(),
|
||||
inventory.blocks().len(),
|
||||
block_sizes_known,
|
||||
);
|
||||
|
||||
wait_for_shutdown().await?;
|
||||
drop(connection);
|
||||
eprintln!("redbear-udisks: received shutdown signal, exiting cleanly");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn main() {
|
||||
match parse_args() {
|
||||
Ok(Command::Help) => {
|
||||
println!("{}", usage());
|
||||
}
|
||||
Ok(Command::Run) => {
|
||||
let runtime = match RuntimeBuilder::new_multi_thread().enable_all().build() {
|
||||
Ok(runtime) => runtime,
|
||||
Err(err) => {
|
||||
eprintln!("redbear-udisks: failed to create tokio runtime: {err}");
|
||||
process::exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
if let Err(err) = runtime.block_on(run_daemon()) {
|
||||
eprintln!("redbear-udisks: fatal error: {err}");
|
||||
process::exit(1);
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
eprintln!("redbear-udisks: {err}");
|
||||
eprintln!("{}", usage());
|
||||
process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user