Migrate pmoutils to registry and refactor dependencies
Migrate pmoutils crate to registry version 0.1.2 and update all dependencies to use the registry version instead of local path references. Remove pmoutils from Cargo.lock and Cargo.toml files where it was previously used as a local path dependency. Move ToXmlElement trait to pmodidl crate and update all usages to import from pmodidl instead of pmoutils. Fix parameter order in find_process_using_port function call.
This commit is contained in:
7
Cargo.lock
generated
7
Cargo.lock
generated
@@ -4047,7 +4047,6 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"bevy_reflect",
|
||||
"bevy_reflect_derive",
|
||||
"pmoutils",
|
||||
"quick-xml",
|
||||
"serde",
|
||||
"utoipa",
|
||||
@@ -4362,15 +4361,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pmoutils"
|
||||
version = "0.1.0"
|
||||
version = "0.1.2"
|
||||
source = "sparse+https://gargoton.petite-maison-orange.fr/api/packages/pmo/cargo/"
|
||||
checksum = "9ed68fb06ffdd1a30fc7792d883dcd6d61ad7705c3a744f54a0a5bcc341fc65e"
|
||||
dependencies = [
|
||||
"get_if_addrs",
|
||||
"netstat2",
|
||||
"os_info",
|
||||
"quick-xml",
|
||||
"sysinfo",
|
||||
"users",
|
||||
"xmltree 0.10.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
@@ -7,7 +7,6 @@ members = [
|
||||
"pmowebrenderer",
|
||||
"pmomediaserver",
|
||||
"pmoconfig",
|
||||
"pmoutils",
|
||||
"pmodidl",
|
||||
"pmoserver",
|
||||
"pmoapp",
|
||||
|
||||
@@ -5,7 +5,7 @@ version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
pmoutils ={ path = "../pmoutils" }
|
||||
pmoutils = { version = "0.1.2", registry = "pmo" }
|
||||
|
||||
serde = { workspace = true }
|
||||
serde_yaml = { workspace = true }
|
||||
|
||||
@@ -6,7 +6,7 @@ edition = "2024"
|
||||
[dependencies]
|
||||
pmoupnp = { path = "../pmoupnp" }
|
||||
pmodidl = { path = "../pmodidl" }
|
||||
pmoutils = { path = "../pmoutils" }
|
||||
pmoutils = { version = "0.1.2", registry = "pmo" }
|
||||
quick-xml = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
ureq = "3.1.4"
|
||||
|
||||
@@ -1562,7 +1562,7 @@ pub(crate) fn build_didl_lite_metadata(
|
||||
protocol_info: &str,
|
||||
) -> String {
|
||||
use pmodidl::{DIDLLite, Item, Resource};
|
||||
use pmoutils::ToXmlElement;
|
||||
use pmodidl::ToXmlElement;
|
||||
|
||||
// Construire l'Item DIDL avec toutes les métadonnées
|
||||
let item = Item {
|
||||
|
||||
@@ -10,5 +10,4 @@ utoipa-swagger-ui = { version = "9.0.2", features = ["axum"] }
|
||||
quick-xml = { workspace = true }
|
||||
bevy_reflect = "0.17.1"
|
||||
bevy_reflect_derive = "0.17.1"
|
||||
pmoutils = { path = "../pmoutils" }
|
||||
xmltree = "0.10"
|
||||
|
||||
@@ -3,14 +3,31 @@
|
||||
//! Parser et utilitaires pour le format DIDL-Lite utilisé dans UPnP/DLNA.
|
||||
|
||||
use bevy_reflect::Reflect;
|
||||
use pmoutils::ToXmlElement;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::borrow::Cow;
|
||||
use std::collections::HashSet;
|
||||
use std::fmt::Write;
|
||||
use std::io::Cursor;
|
||||
use std::time::SystemTime;
|
||||
use xmltree::{Element, XMLNode};
|
||||
use xmltree::{Element, EmitterConfig, XMLNode};
|
||||
|
||||
/// Trait générique pour obtenir un élément XML (xmltree::Element).
|
||||
pub trait ToXmlElement {
|
||||
/// Convertit l'objet en élément XML.
|
||||
fn to_xml_element(&self) -> Element;
|
||||
|
||||
/// Sérialise en chaîne XML formatée.
|
||||
fn to_xml(&self) -> String {
|
||||
let elem = self.to_xml_element();
|
||||
let config = EmitterConfig::new()
|
||||
.perform_indent(true)
|
||||
.indent_string(" ");
|
||||
let mut buf = Vec::new();
|
||||
elem.write_with_config(&mut buf, config)
|
||||
.expect("Failed to write XML");
|
||||
String::from_utf8(buf).expect("Invalid UTF-8")
|
||||
}
|
||||
}
|
||||
|
||||
// ============= Couche d'abstraction générique =============
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ quick-xml = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
pmoutils = { path = "../pmoutils" }
|
||||
pmoutils = { version = "0.1.2", registry = "pmo" }
|
||||
|
||||
# Optional dependencies
|
||||
axum = { version = "0.8", optional = true }
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
use pmodidl::{Container, DIDLLite};
|
||||
use pmosource::api::{get_source as get_source_from_registry, list_all_sources};
|
||||
use pmosource::{BrowseResult, MusicSource, MusicSourceError};
|
||||
use pmoutils::ToXmlElement;
|
||||
use pmodidl::ToXmlElement;
|
||||
use std::collections::HashSet;
|
||||
use std::sync::Arc;
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ edition = "2024"
|
||||
[dependencies]
|
||||
pmoconfig = { path = "../pmoconfig" }
|
||||
pmodidl = { path = "../pmodidl"}
|
||||
pmoutils = { path = "../pmoutils" }
|
||||
pmoutils = { version = "0.1.2", registry = "pmo" }
|
||||
pmoserver = { path = "../pmoserver" }
|
||||
pmocovers = { path = "../pmocovers", features = ["pmoserver"] }
|
||||
pmoaudiocache = { path = "../pmoaudiocache", features = ["pmoserver"] }
|
||||
|
||||
@@ -430,7 +430,7 @@ impl UpnpServerExt for Server {
|
||||
let kind = e.kind();
|
||||
if kind == std::io::ErrorKind::AddrInUse {
|
||||
let port = crate::ssdp::SSDP_PORT;
|
||||
if let Some(process) = find_process_using_port(port, TransportProtocol::Udp) {
|
||||
if let Some(process) = find_process_using_port(TransportProtocol::Udp, port) {
|
||||
error!(
|
||||
"❌ SSDP initialization failed: port {} is already in use by \
|
||||
PID {} ({}) owned by {}: {}",
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
[package]
|
||||
name = "pmoutils"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
get_if_addrs = "0.5.3"
|
||||
os_info = "3.8"
|
||||
netstat2 = "0.11"
|
||||
sysinfo = "0.30"
|
||||
users = "0.11"
|
||||
quick-xml = { workspace = true }
|
||||
xmltree = "0.10"
|
||||
@@ -1,13 +0,0 @@
|
||||
[package]
|
||||
name = "pmoutils"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
get_if_addrs = "0.5.3"
|
||||
os_info = "3.8"
|
||||
netstat2 = "0.11"
|
||||
sysinfo = "0.30"
|
||||
users = "0.11"
|
||||
quick-xml = "0.38.3"
|
||||
xmltree = "0.10"
|
||||
@@ -1,273 +0,0 @@
|
||||
use get_if_addrs::get_if_addrs;
|
||||
use std::net::UdpSocket;
|
||||
|
||||
/// Devine l'adresse IP locale de la machine.
|
||||
///
|
||||
/// Cette fonction tente de déterminer l'adresse IP locale en créant une connexion UDP
|
||||
/// vers un serveur DNS public (8.8.8.8). Cette technique permet d'identifier l'interface
|
||||
/// réseau qui serait utilisée pour communiquer avec Internet.
|
||||
///
|
||||
/// # Fonctionnement
|
||||
///
|
||||
/// 1. Crée un socket UDP lié à `0.0.0.0:0` (n'importe quelle interface, port aléatoire)
|
||||
/// 2. Tente une connexion (non effective pour UDP) vers `8.8.8.8:80`
|
||||
/// 3. Récupère l'adresse IP locale du socket
|
||||
/// 4. En cas d'échec à n'importe quelle étape, retourne `127.0.0.1`
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// Retourne l'adresse IP locale sous forme de `String`, ou `"127.0.0.1"` en cas d'erreur.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use pmoutils::guess_local_ip;
|
||||
///
|
||||
/// let ip = guess_local_ip();
|
||||
/// println!("IP locale détectée: {}", ip);
|
||||
/// // Affiche par exemple: "IP locale détectée: 192.168.1.42"
|
||||
/// ```
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// Cette méthode ne crée pas de véritable connexion réseau (UDP est sans connexion),
|
||||
/// elle demande simplement au système d'exploitation quelle interface serait utilisée
|
||||
/// pour joindre l'adresse cible.
|
||||
pub fn guess_local_ip() -> String {
|
||||
match UdpSocket::bind("0.0.0.0:0") {
|
||||
Ok(socket) => {
|
||||
if socket.connect("8.8.8.8:80").is_ok() {
|
||||
if let Ok(local_addr) = socket.local_addr() {
|
||||
return local_addr.ip().to_string();
|
||||
}
|
||||
}
|
||||
"127.0.0.1".to_string()
|
||||
}
|
||||
Err(_) => "127.0.0.1".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Liste toutes les adresses IP non-loopback des interfaces réseau.
|
||||
///
|
||||
/// Parcourt toutes les interfaces réseau de la machine et collecte leurs adresses IPv4,
|
||||
/// en excluant les adresses de loopback (127.0.0.1).
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// Retourne une `HashMap` où :
|
||||
/// - **Clé** : nom de l'interface réseau (ex: `"eth0"`, `"wlan0"`, `"en0"`)
|
||||
/// - **Valeur** : vecteur des adresses IP (format String) associées à cette interface
|
||||
///
|
||||
/// En cas d'erreur lors de la récupération des interfaces, retourne une HashMap
|
||||
/// contenant une entrée `"error"` avec un message d'erreur.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use pmoutils::ip_utils::list_all_ips;
|
||||
///
|
||||
/// let ips = list_all_ips();
|
||||
/// for (interface, addresses) in ips {
|
||||
/// println!("Interface {}: {:?}", interface, addresses);
|
||||
/// }
|
||||
/// // Affiche par exemple:
|
||||
/// // Interface eth0: ["192.168.1.42"]
|
||||
/// // Interface wlan0: ["10.0.0.15"]
|
||||
/// ```
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// - Seules les adresses IPv4 sont retournées
|
||||
/// - Les adresses de loopback (127.x.x.x) sont filtrées
|
||||
/// - Les adresses IPv6 sont ignorées
|
||||
pub fn list_all_ips() -> std::collections::HashMap<String, Vec<String>> {
|
||||
let mut result = std::collections::HashMap::new();
|
||||
|
||||
if let Ok(interfaces) = get_if_addrs() {
|
||||
for iface in interfaces {
|
||||
let ip = iface.ip();
|
||||
if ip.is_loopback() {
|
||||
continue;
|
||||
}
|
||||
if ip.is_ipv4() {
|
||||
result
|
||||
.entry(iface.name)
|
||||
.or_insert_with(Vec::new)
|
||||
.push(ip.to_string());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
result.insert(
|
||||
"error".to_string(),
|
||||
vec!["Failed to get interfaces".to_string()],
|
||||
);
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::net::IpAddr;
|
||||
|
||||
#[test]
|
||||
fn test_guess_local_ip_returns_valid_ip() {
|
||||
let ip = guess_local_ip();
|
||||
|
||||
// Vérifie que le résultat est parsable comme une IP
|
||||
assert!(
|
||||
ip.parse::<IpAddr>().is_ok(),
|
||||
"Should return a valid IP address"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_guess_local_ip_not_empty() {
|
||||
let ip = guess_local_ip();
|
||||
|
||||
assert!(!ip.is_empty(), "IP should not be empty");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_guess_local_ip_is_ipv4() {
|
||||
let ip = guess_local_ip();
|
||||
|
||||
if let Ok(parsed_ip) = ip.parse::<IpAddr>() {
|
||||
assert!(parsed_ip.is_ipv4(), "Should return an IPv4 address");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_guess_local_ip_fallback_is_localhost() {
|
||||
// Ce test vérifie que si aucune IP n'est trouvée, on retourne 127.0.0.1
|
||||
// (difficile à tester sans mocker, mais on vérifie la cohérence)
|
||||
let ip = guess_local_ip();
|
||||
let parsed = ip.parse::<IpAddr>().unwrap();
|
||||
|
||||
// L'IP doit être soit locale (127.0.0.1) soit une IP privée valide
|
||||
assert!(
|
||||
parsed.is_loopback() || is_private_ip(&ip),
|
||||
"IP should be either loopback or private"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_list_all_ips_no_loopback() {
|
||||
let ips = list_all_ips();
|
||||
|
||||
// Vérifie qu'aucune adresse de loopback n'est présente
|
||||
for (_, addresses) in ips.iter() {
|
||||
for addr in addresses {
|
||||
if let Ok(parsed_ip) = addr.parse::<IpAddr>() {
|
||||
assert!(
|
||||
!parsed_ip.is_loopback(),
|
||||
"Loopback addresses should be filtered out"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_list_all_ips_only_ipv4() {
|
||||
let ips = list_all_ips();
|
||||
|
||||
// Vérifie que seules des adresses IPv4 sont retournées
|
||||
for (iface_name, addresses) in ips.iter() {
|
||||
if iface_name == "error" {
|
||||
continue; // Skip error entries
|
||||
}
|
||||
|
||||
for addr in addresses {
|
||||
if let Ok(parsed_ip) = addr.parse::<IpAddr>() {
|
||||
assert!(
|
||||
parsed_ip.is_ipv4(),
|
||||
"Only IPv4 addresses should be returned"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_list_all_ips_valid_format() {
|
||||
let ips = list_all_ips();
|
||||
|
||||
// Vérifie que toutes les IPs sont dans un format valide
|
||||
for (iface_name, addresses) in ips.iter() {
|
||||
if iface_name == "error" {
|
||||
continue;
|
||||
}
|
||||
|
||||
for addr in addresses {
|
||||
assert!(
|
||||
addr.parse::<IpAddr>().is_ok(),
|
||||
"Each IP should be in valid format: {}",
|
||||
addr
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_list_all_ips_interface_names_not_empty() {
|
||||
let ips = list_all_ips();
|
||||
|
||||
// Vérifie que les noms d'interface ne sont pas vides
|
||||
for (iface_name, _) in ips.iter() {
|
||||
assert!(
|
||||
!iface_name.is_empty(),
|
||||
"Interface names should not be empty"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_list_all_ips_no_duplicate_ips_per_interface() {
|
||||
let ips = list_all_ips();
|
||||
|
||||
// Vérifie qu'il n'y a pas de doublons par interface
|
||||
for (iface_name, addresses) in ips.iter() {
|
||||
if iface_name == "error" {
|
||||
continue;
|
||||
}
|
||||
|
||||
let unique_addresses: std::collections::HashSet<_> = addresses.iter().collect();
|
||||
assert_eq!(
|
||||
addresses.len(),
|
||||
unique_addresses.len(),
|
||||
"No duplicate IPs should exist for interface {}",
|
||||
iface_name
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Fonction helper pour les tests
|
||||
fn is_private_ip(ip_str: &str) -> bool {
|
||||
if let Ok(ip) = ip_str.parse::<IpAddr>() {
|
||||
match ip {
|
||||
IpAddr::V4(ipv4) => {
|
||||
let octets = ipv4.octets();
|
||||
// Plages privées: 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16
|
||||
octets[0] == 10
|
||||
|| (octets[0] == 172 && octets[1] >= 16 && octets[1] <= 31)
|
||||
|| (octets[0] == 192 && octets[1] == 168)
|
||||
}
|
||||
IpAddr::V6(_) => false,
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_helper_is_private_ip() {
|
||||
// Tests pour la fonction helper
|
||||
assert!(is_private_ip("10.0.0.1"));
|
||||
assert!(is_private_ip("172.16.0.1"));
|
||||
assert!(is_private_ip("192.168.1.1"));
|
||||
assert!(!is_private_ip("8.8.8.8"));
|
||||
assert!(!is_private_ip("127.0.0.1")); // loopback n'est pas "privé" au sens réseau local
|
||||
}
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
/// Utilitaires pour la gestion des adresses IP réseau.
|
||||
///
|
||||
/// Ce module fournit des fonctions pour détecter et lister les adresses IP
|
||||
/// des interfaces réseau locales de la machine.
|
||||
///
|
||||
/// # Fonctions principales
|
||||
///
|
||||
/// - [`guess_local_ip`] : Devine l'adresse IP locale utilisée pour les connexions sortantes
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use pmoutils::guess_local_ip;
|
||||
///
|
||||
/// let ip = guess_local_ip();
|
||||
/// println!("Adresse IP locale: {}", ip);
|
||||
/// ```
|
||||
pub mod ip_utils;
|
||||
|
||||
pub use ip_utils::guess_local_ip;
|
||||
pub mod process;
|
||||
pub use process::{ProcessPortInfo, TransportProtocol, find_process_using_port};
|
||||
use xmltree::{Element, EmitterConfig};
|
||||
|
||||
/// Retourne une chaîne décrivant le système d'exploitation et sa version.
|
||||
///
|
||||
/// Utilise la crate `os_info` pour obtenir de manière portable et fiable
|
||||
/// les informations sur le système d'exploitation courant.
|
||||
///
|
||||
/// # Format
|
||||
/// - macOS: "macOS/15.1" ou "Mac OS/10.15.7"
|
||||
/// - Linux: "Linux/6.5.0" ou "Ubuntu/22.04"
|
||||
/// - Windows: "Windows/10.0.19045"
|
||||
/// - Autre: "{OS}/Unknown"
|
||||
///
|
||||
/// # Exemples
|
||||
///
|
||||
/// ```
|
||||
/// use pmoutils::get_os_string;
|
||||
///
|
||||
/// let os = get_os_string();
|
||||
/// println!("OS: {}", os); // Ex: "Linux/6.5.0"
|
||||
/// ```
|
||||
pub fn get_os_string() -> String {
|
||||
let info = os_info::get();
|
||||
let os_type = format!("{:?}", info.os_type());
|
||||
|
||||
// Obtenir la version si disponible
|
||||
let version = info.version();
|
||||
if version != &os_info::Version::Unknown {
|
||||
format!("{}/{}", os_type, version)
|
||||
} else {
|
||||
format!("{}/Unknown", os_type)
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait générique pour obtenir un élément XML (xmltree::Element).
|
||||
///
|
||||
/// Aligné sur la signature utilisée dans pmoupnp (UpnpObject::to_xml_element),
|
||||
/// afin de pouvoir factoriser la sérialisation XML entre crates.
|
||||
pub trait ToXmlElement {
|
||||
/// Convertit l'objet en élément XML.
|
||||
fn to_xml_element(&self) -> Element;
|
||||
|
||||
/// Sérialise en chaîne XML formatée.
|
||||
fn to_xml(&self) -> String {
|
||||
let elem = self.to_xml_element();
|
||||
let config = EmitterConfig::new()
|
||||
.perform_indent(true)
|
||||
.indent_string(" ");
|
||||
let mut buf = Vec::new();
|
||||
elem.write_with_config(&mut buf, config)
|
||||
.expect("Failed to write XML");
|
||||
String::from_utf8(buf).expect("Invalid UTF-8")
|
||||
}
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
use netstat2::{AddressFamilyFlags, ProtocolFlags, ProtocolSocketInfo, get_sockets_info};
|
||||
use sysinfo::{Pid, System};
|
||||
|
||||
/// Informations sur un processus utilisant un port réseau.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ProcessPortInfo {
|
||||
pub pid: u32,
|
||||
pub process_name: String,
|
||||
pub owner: String,
|
||||
pub port: u16,
|
||||
}
|
||||
|
||||
/// Protocole de transport utilisé pour la recherche.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum TransportProtocol {
|
||||
Tcp,
|
||||
Udp,
|
||||
}
|
||||
|
||||
/// Tente de trouver le processus qui écoute sur `port` pour le protocole donné.
|
||||
///
|
||||
/// Retourne `Some(ProcessPortInfo)` si un processus a pu être identifié, sinon `None`.
|
||||
pub fn find_process_using_port(port: u16, protocol: TransportProtocol) -> Option<ProcessPortInfo> {
|
||||
let proto_flag = match protocol {
|
||||
TransportProtocol::Tcp => ProtocolFlags::TCP,
|
||||
TransportProtocol::Udp => ProtocolFlags::UDP,
|
||||
};
|
||||
|
||||
let sockets = get_sockets_info(
|
||||
AddressFamilyFlags::IPV4 | AddressFamilyFlags::IPV6,
|
||||
proto_flag,
|
||||
)
|
||||
.ok()?;
|
||||
|
||||
// Préparer l'inspection des processus.
|
||||
let mut system = System::new_all();
|
||||
system.refresh_all();
|
||||
|
||||
for socket in sockets {
|
||||
match socket.protocol_socket_info {
|
||||
ProtocolSocketInfo::Tcp(ref tcp_info)
|
||||
if matches!(protocol, TransportProtocol::Tcp) && tcp_info.local_port == port =>
|
||||
{
|
||||
if let Some(info) =
|
||||
build_process_info(&mut system, port, socket.associated_pids.first())
|
||||
{
|
||||
return Some(info);
|
||||
}
|
||||
}
|
||||
ProtocolSocketInfo::Udp(ref udp_info)
|
||||
if matches!(protocol, TransportProtocol::Udp) && udp_info.local_port == port =>
|
||||
{
|
||||
if let Some(info) =
|
||||
build_process_info(&mut system, port, socket.associated_pids.first())
|
||||
{
|
||||
return Some(info);
|
||||
}
|
||||
}
|
||||
_ => continue,
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn build_process_info(
|
||||
system: &mut System,
|
||||
port: u16,
|
||||
pid_opt: Option<&u32>,
|
||||
) -> Option<ProcessPortInfo> {
|
||||
let pid = *pid_opt?;
|
||||
let process = system.process(Pid::from_u32(pid))?;
|
||||
let process_name = process.name().to_string();
|
||||
|
||||
let owner = process
|
||||
.user_id()
|
||||
.and_then(|uid| {
|
||||
users::get_user_by_uid(**uid).map(|user| user.name().to_string_lossy().into_owned())
|
||||
})
|
||||
.unwrap_or_else(|| "unknown".to_string());
|
||||
|
||||
Some(ProcessPortInfo {
|
||||
pid,
|
||||
process_name,
|
||||
owner,
|
||||
port,
|
||||
})
|
||||
}
|
||||
@@ -40,7 +40,7 @@ thiserror = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
|
||||
pmodidl = { path = "../pmodidl" }
|
||||
pmoutils = { path = "../pmoutils" }
|
||||
pmoutils = { version = "0.1.2", registry = "pmo" }
|
||||
|
||||
[features]
|
||||
default = []
|
||||
|
||||
@@ -8,7 +8,7 @@ use std::sync::Arc;
|
||||
use pmodidl::DIDLLite;
|
||||
use pmoupnp::actions::{ActionData, ActionError, ActionHandler, get_value};
|
||||
use pmoupnp::{get, set};
|
||||
use pmoutils::ToXmlElement;
|
||||
use pmodidl::ToXmlElement;
|
||||
|
||||
use crate::messages::PlaybackState;
|
||||
use crate::pipeline::{PipelineControl, PipelineHandle, seconds_to_upnp_time, upnp_time_to_seconds};
|
||||
|
||||
Reference in New Issue
Block a user