diff --git a/src/core/config.rs b/src/core/config.rs index 460655c7..3a89b5b6 100644 --- a/src/core/config.rs +++ b/src/core/config.rs @@ -630,6 +630,40 @@ pub enum NetListenConfig { Unix(UnixListenConfig), } +impl NetListenConfig { + /// Parse config file prometheus_port format. + pub fn from_prometheus_port_str(s: &str) -> Result { + if let Some(path) = s.strip_prefix("ipc://") { + if path.is_empty() { + return Err(format!("invalid listen address '{s}'; ipc path is empty")); + } + + Ok(Self::Unix(UnixListenConfig { + path: path.into(), + mode: None, + user: None, + group: None, + params: HashMap::new(), + })) + } else { + let addr = if let Ok(addr) = s.parse::() { + addr + } else if let Ok(port) = s.parse::() { + std::net::SocketAddr::from(([0, 0, 0, 0], port)) + } else { + return Err(format!( + "invalid listen address '{s}'; expected ipc://PATH, IP:PORT, or PORT" + )); + }; + + Ok(Self::Tcp(TcpListenConfig { + addr, + params: HashMap::new(), + })) + } + } +} + impl FromStr for NetListenConfig { type Err = String; diff --git a/src/core/prometheus.rs b/src/core/prometheus.rs index 3f5f375a..38728303 100644 --- a/src/core/prometheus.rs +++ b/src/core/prometheus.rs @@ -281,31 +281,13 @@ async fn handle_connection( pub mod ffi { use super::*; + use crate::core::config::NetListenConfig; use libc::c_char; use std::ffi::{CStr, CString}; /// Opaque handle to a `prometheus::Registry`, for use across the FFI boundary. pub enum PrometheusRegistry {} - fn parse_listen_addr(addr: &str) -> Result { - if let Some(path) = addr.strip_prefix("ipc://") { - let l = mio::net::UnixListener::bind(path) - .map_err(|e| format!("failed to bind {path}: {e}"))?; - Ok(NetListener::Unix(l)) - } else if let Ok(socket_addr) = addr.parse::() { - let l = mio::net::TcpListener::bind(socket_addr) - .map_err(|e| format!("failed to bind {socket_addr}: {e}"))?; - Ok(NetListener::Tcp(l)) - } else if let Ok(port) = addr.parse::() { - let socket_addr = std::net::SocketAddr::from(([0, 0, 0, 0], port)); - let l = mio::net::TcpListener::bind(socket_addr) - .map_err(|e| format!("failed to bind {socket_addr}: {e}"))?; - Ok(NetListener::Tcp(l)) - } else { - Err(format!("invalid listen address: {addr}")) - } - } - /// Create and start a prometheus HTTP server listening on `addr`. The provided `registry` is /// cloned internally so the server is independent of the registry's lifetime. Returns an opaque /// handle; call `prometheus_server_destroy` when done. On failure, returns null and writes a @@ -322,10 +304,27 @@ pub mod ffi { registry: *const PrometheusRegistry, error: *mut *const c_char, ) -> *mut PrometheusServer { - let addr = CStr::from_ptr(addr).to_str().expect("invalid addr string"); + let addr = match CStr::from_ptr(addr).to_str() { + Ok(addr) => addr, + Err(e) => { + *error = CString::new(format!("invalid listen address: {e}")) + .unwrap_or_default() + .into_raw(); + return std::ptr::null_mut(); + } + }; + let registry = &*(registry as *const prometheus::Registry); - let listener = match parse_listen_addr(addr) { + let config = match NetListenConfig::from_prometheus_port_str(addr) { + Ok(c) => c, + Err(e) => { + *error = CString::new(e).unwrap_or_default().into_raw(); + return std::ptr::null_mut(); + } + }; + + let listener = match NetListener::bind_config(&config) { Ok(l) => l, Err(e) => { *error = CString::new(e).unwrap_or_default().into_raw();