246 lines
6.9 KiB
Rust
246 lines
6.9 KiB
Rust
// Copyright (C) 2022 Maxime “pep” Buquet <pep@bouah.net>
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify it
|
|
// under the terms of the GNU Affero General Public License as published by the
|
|
// Free Software Foundation, either version 3 of the License, or (at your
|
|
// option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful, but WITHOUT
|
|
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License
|
|
// for more details.
|
|
//
|
|
// You should have received a copy of the GNU Affero General Public License
|
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
pub mod error;
|
|
pub mod mm;
|
|
|
|
use error::Error;
|
|
use mm::modem::Modem as ModemAccess;
|
|
|
|
use std::fmt;
|
|
use std::sync::Arc;
|
|
use std::str::FromStr;
|
|
use std::env::args;
|
|
|
|
use env_logger;
|
|
|
|
use futures_util::StreamExt;
|
|
use tokio::{
|
|
task::JoinHandle,
|
|
time::Duration,
|
|
};
|
|
|
|
use mmdbus::{
|
|
dbus::{
|
|
message::MatchRule,
|
|
nonblock::{
|
|
SyncConnection, Proxy,
|
|
stdintf::org_freedesktop_dbus::ObjectManager,
|
|
},
|
|
strings::{Path as DBusPath},
|
|
},
|
|
};
|
|
|
|
use dbus_tokio::connection;
|
|
|
|
use xmpp::{ClientBuilder, ClientType};
|
|
use xmpp_parsers::{
|
|
Jid, BareJid,
|
|
message::MessageType,
|
|
};
|
|
|
|
// The following source has been used as an example:
|
|
// https://github.com/soerenmeier/linux-info/blob/master/src/network/modem_manager.rs
|
|
|
|
const MM_NAME: &str = "org.freedesktop.ModemManager1";
|
|
const MM_PATH: &str = "/org/freedesktop/ModemManager1";
|
|
const TIMEOUT: Duration = Duration::from_secs(5);
|
|
|
|
#[derive(Clone)]
|
|
pub struct DBus {
|
|
pub conn: Arc<SyncConnection>,
|
|
pub handle: Arc<JoinHandle<()>>,
|
|
}
|
|
|
|
impl DBus {
|
|
pub fn connect() -> Result<Self, Error> {
|
|
let (resource, conn) = connection::new_system_sync()?;
|
|
|
|
let handle: JoinHandle<()> = tokio::spawn(async {
|
|
let err = resource.await;
|
|
panic!("Lost connection to D-Bus: {}", err);
|
|
});
|
|
|
|
Ok(Self { conn, handle: Arc::new(handle) })
|
|
}
|
|
|
|
pub fn proxy<'a, 'b, P>(&'b self, path: P) -> Proxy<'a, &'b SyncConnection>
|
|
where
|
|
P: Into<DBusPath<'a>>,
|
|
{
|
|
Proxy::new(MM_NAME, path, TIMEOUT, &*self.conn)
|
|
}
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
pub struct ModemManager {
|
|
dbus: DBus,
|
|
}
|
|
|
|
impl ModemManager {
|
|
pub fn connect() -> Result<Self, Error> {
|
|
DBus::connect().map(|dbus| Self { dbus })
|
|
}
|
|
|
|
pub async fn modems(&self) -> Result<Vec<Modem>, Error> {
|
|
let objects = self.dbus.proxy(MM_PATH).get_managed_objects().await?;
|
|
let modems = objects
|
|
.into_iter()
|
|
.map(|(path, _)| {
|
|
Modem {
|
|
dbus: self.dbus.clone(),
|
|
path,
|
|
}
|
|
})
|
|
.collect();
|
|
|
|
Ok(modems)
|
|
}
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
pub struct Modem {
|
|
dbus: DBus,
|
|
pub path: DBusPath<'static>,
|
|
}
|
|
|
|
impl fmt::Debug for Modem {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
write!(f, "Modem {{ {}, {:?}, .. }}", self.path, "1234")
|
|
}
|
|
}
|
|
|
|
impl Modem {
|
|
pub async fn enabled(&self) -> Result<bool, Error> {
|
|
let state: ModemState = ModemAccess::state(&self.dbus.proxy(&self.path)).await.map(Into::into)?;
|
|
Ok(match state {
|
|
ModemState::Enabling
|
|
| ModemState::Enabled
|
|
| ModemState::Searching
|
|
| ModemState::Registered
|
|
| ModemState::Connecting
|
|
| ModemState::Connected => true,
|
|
_ => false,
|
|
})
|
|
}
|
|
|
|
pub async fn model(&self) -> Result<String, Error> {
|
|
ModemAccess::model(&self.dbus.proxy(&self.path)).await.map_err(Error::DBusError)
|
|
}
|
|
}
|
|
|
|
#[repr(i32)]
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
pub enum ModemState {
|
|
/// The modem is unusable.
|
|
Failed = -1,
|
|
/// State unknown or not reportable.
|
|
Unknown = 0,
|
|
/// The modem is currently being initialized.
|
|
Initializing = 1,
|
|
/// The modem needs to be unlocked.
|
|
Locked = 2,
|
|
/// The modem is not enabled and is powered down.
|
|
Disabled = 3,
|
|
/// The modem is currently transitioning to the MM_MODEM_STATE_DISABLED
|
|
/// state.
|
|
Disabling = 4,
|
|
/// The modem is currently transitioning to the MM_MODEM_STATE_ENABLED
|
|
/// state.
|
|
Enabling = 5,
|
|
/// The modem is enabled and powered on but not registered with a network
|
|
/// provider and not available for data connections.
|
|
Enabled = 6,
|
|
/// The modem is searching for a network provider to register with.
|
|
Searching = 7,
|
|
/// The modem is registered with a network provider, and data connections
|
|
/// and messaging may be available for use.
|
|
Registered = 8,
|
|
/// The modem is disconnecting and deactivating the last active packet data
|
|
/// bearer. This state will not be entered if more than one packet data
|
|
/// bearer is active and one of the active bearers is deactivated.
|
|
Disconnecting = 9,
|
|
/// The modem is activating and connecting the first packet data bearer.
|
|
/// Subsequent bearer activations when another bearer is already active
|
|
/// do not cause this state to be entered.
|
|
Connecting = 10,
|
|
/// One or more packet data bearers is active and connected.
|
|
Connected = 11,
|
|
}
|
|
|
|
impl From<i32> for ModemState {
|
|
fn from(num: i32) -> Self {
|
|
if num < -1 || num > 11 {
|
|
Self::Unknown
|
|
} else {
|
|
unsafe { *(&num as *const i32 as *const Self) }
|
|
}
|
|
}
|
|
}
|
|
|
|
async fn print_foo(mm: ModemManager) -> Result<(), Error> {
|
|
let modems = mm.modems().await?;
|
|
println!("Modems: {:?}", modems);
|
|
for modem in modems {
|
|
println!("Modem: {:?}", modem);
|
|
println!("Enabled: {:?}", modem.enabled().await);
|
|
println!("Model: {:?}", modem.model().await);
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::main]
|
|
pub async fn main() -> Result<(), Error> {
|
|
env_logger::init();
|
|
|
|
let args: Vec<String> = args().collect();
|
|
if args.len() != 3 {
|
|
println!("Usage: {} <jid> <password>", args[0]);
|
|
return Err(Error::InvalidArgs)
|
|
}
|
|
|
|
// ModemManager
|
|
let dbus = DBus::connect()?;
|
|
|
|
let mut interval = tokio::time::interval(Duration::from_secs(2));
|
|
|
|
let mr = MatchRule::new_signal("org.freedesktop.DBus.Properties", "PropertiesChanged");
|
|
|
|
|
|
// XMPP
|
|
let jid_str = args[1].as_str();
|
|
let jid = Jid::Bare(BareJid::from_str(jid_str).unwrap());
|
|
let passwd = args[2].as_str();
|
|
|
|
// Client instance
|
|
let mut xmpp = ClientBuilder::new(jid_str, passwd)
|
|
.set_client(ClientType::Bot, "gateway_mm")
|
|
.set_website("https://codeberg.org/pep./gateway_mm")
|
|
.build()
|
|
.unwrap();
|
|
|
|
loop {
|
|
tokio::select! {
|
|
events = xmpp.wait_for_events() => {
|
|
println!("Events: {:?}", events);
|
|
},
|
|
_ = interval.tick() => {
|
|
let mm = ModemManager { dbus: dbus.clone() };
|
|
let _ = print_foo(mm).await;
|
|
},
|
|
}
|
|
}
|
|
}
|