diff --git a/libsession/libsession-rs/src/lib.rs b/libsession/libsession-rs/src/lib.rs index df661d9..95437d5 100644 --- a/libsession/libsession-rs/src/lib.rs +++ b/libsession/libsession-rs/src/lib.rs @@ -36,8 +36,7 @@ impl Context { libsession_sys::libsession_get_session(self.0.as_ptr(), &raw mut id) })?; - let id = ptr::NonNull::new(id).ok_or(libsession_sys::Error::InternalError)?; - Ok(Session(id)) + Session::new(id) } /// Retrieves the session ID for a process. @@ -52,8 +51,7 @@ impl Context { libsession_sys::libsession_pid_get_session(self.0.as_ptr(), pid, &raw mut id) })?; - let id = ptr::NonNull::new(id).ok_or(libsession_sys::Error::InternalError)?; - Ok(Session(id)) + Session::new(id) } pub fn get_display_for_uid(&self, uid: u32) -> libsession_sys::Result { @@ -62,8 +60,7 @@ impl Context { libsession_sys::libsession_uid_get_display(self.0.as_ptr(), uid, &raw mut id) })?; - let id = ptr::NonNull::new(id).ok_or(libsession_sys::Error::InternalError)?; - Ok(Session(id)) + Session::new(id) } pub fn list_sessions(&self) -> libsession_sys::Result> { @@ -87,12 +84,16 @@ impl Context { let ids = unsafe { std::slice::from_raw_parts_mut(sessions.as_ptr(), count) }; let mut result = Vec::with_capacity(count); for id in ids { - let Some(session) = ptr::NonNull::new(*id) else { - unsafe { libsession_sys::libsession_session_list_free(sessions.as_ptr(), count) }; - return Err(libsession_sys::Error::InternalError); + let session = match Session::new(*id) { + Ok(session) => session, + Err(error) => { + unsafe { + libsession_sys::libsession_session_list_free(sessions.as_ptr(), count) + }; + return Err(error); + } }; - result.push(Session(session)); - // Transfer ownership of the handle while leaving the array to the library. + result.push(session); *id = ptr::null_mut(); } unsafe { libsession_sys::libsession_session_list_free(sessions.as_ptr(), count) }; @@ -106,7 +107,7 @@ impl Context { libsession_sys::Result::<()>::from(unsafe { libsession_sys::libsession_session_is_local( self.0.as_ptr(), - session.0.as_ptr(), + session.inner.as_ptr(), &raw mut is_local, ) })?; @@ -143,7 +144,7 @@ impl Context { libsession_sys::Result::<()>::from(unsafe { libsession_sys::libsession_session_get_uid( self.0.as_ptr(), - session.0.as_ptr(), + session.as_ptr(), &raw mut uid, ) })?; @@ -160,7 +161,7 @@ impl Context { libsession_sys::Result::<()>::from(unsafe { libsession_sys::libsession_session_get_state( self.0.as_ptr(), - session.0.as_ptr(), + session.inner.as_ptr(), &raw mut state, ) })?; @@ -174,7 +175,7 @@ impl Context { libsession_sys::Result::<()>::from(unsafe { libsession_sys::libsession_session_is_active( self.0.as_ptr(), - session.0.as_ptr(), + session.inner.as_ptr(), &raw mut is_active, ) })?; @@ -235,28 +236,47 @@ impl Context { } } -pub struct Session(ptr::NonNull); +pub struct Session { + id: String, + inner: ptr::NonNull, +} -impl Session { - pub fn id(&self) -> libsession_sys::Result { - let mut id = ptr::null_mut(); +impl<'a> Session { + fn new(session: *mut libsession_sys::LibsessionSession) -> libsession_sys::Result { + let session = ptr::NonNull::new(session).ok_or(libsession_sys::Error::InternalError)?; + + let mut id_ptr = ptr::null_mut(); libsession_sys::Result::<()>::from(unsafe { - libsession_sys::libsession_session_get_id(self.0.as_ptr(), &raw mut id) + libsession_sys::libsession_session_get_id(session.as_ptr(), &raw mut id_ptr) })?; - let id = ptr::NonNull::new(id).ok_or(libsession_sys::Error::InternalError)?; - let result = unsafe { ffi::CStr::from_ptr(id.as_ptr()) } + let id_ptr = ptr::NonNull::new(id_ptr).ok_or(libsession_sys::Error::InternalError)?; + + let id_result = unsafe { ffi::CStr::from_ptr(id_ptr.as_ptr()) } .to_str() .map(str::to_owned) - .map_err(|_| libsession_sys::Error::InvalidData); - unsafe { libsession_sys::libsession_session_id_free(id.as_ptr()) }; - result + .map_err(|_| libsession_sys::Error::InvalidData)?; + + unsafe { libsession_sys::libsession_session_id_free(id_ptr.as_ptr()) }; + + Ok(Self { + inner: session, + id: id_result, + }) + } + + fn as_ptr(&self) -> *mut libsession_sys::LibsessionSession { + self.inner.as_ptr() + } + + pub fn id(&'a self) -> &'a str { + &self.id } } impl Drop for Session { fn drop(&mut self) { - unsafe { libsession_sys::libsession_session_free(self.0.as_ptr()) }; + unsafe { libsession_sys::libsession_session_free(self.as_ptr()) }; } } diff --git a/libsession/libsession-rs/src/tests.rs b/libsession/libsession-rs/src/tests.rs index 1d13b71..ca8f8b9 100644 --- a/libsession/libsession-rs/src/tests.rs +++ b/libsession/libsession-rs/src/tests.rs @@ -56,22 +56,18 @@ fn connects_to_correct_backend() { fn list_sessions() { let context = Context::connect().expect("no session backend available"); - let current_id = context + let session = context .get_session() - .expect("current process has no session") - .id() - .expect("failed to read current session ID"); - - let ids: Vec<_> = context - .list_sessions() - .expect("failed to list sessions") - .iter() - .map(|session| session.id().expect("failed to read session ID")) - .collect(); + .expect("current process has no session"); + let current_id = session.id(); assert!( - ids.contains(¤t_id), - "current session {current_id:?} missing from session list: {ids:?}" + context + .list_sessions() + .expect("failed to list sessions") + .into_iter() + .any(|session| session.id() == current_id), + "current session {current_id:?} missing from session list" ); } @@ -111,16 +107,13 @@ fn get_display_session() { let uid = context.get_owner_uid_for_pid(pid).unwrap(); let session = context.get_display_for_uid(uid).unwrap(); - let ids: Vec<_> = context - .list_sessions() - .expect("failed to list sessions") - .iter() - .map(|session| session.id().expect("failed to read session ID")) - .collect(); - assert!( - ids.contains(&session.id().expect("failed to read display session ID")), - "current session {:?} missing from session list: {ids:?}", + context + .list_sessions() + .expect("failed to list sessions") + .into_iter() + .any(|id| session.id() == id.id()), + "current session {:?} missing from session list", session.id() ); } diff --git a/libsession/libsession/src/backend/consolekit.rs b/libsession/libsession/src/backend/consolekit.rs index 64a3f8f..be2df56 100644 --- a/libsession/libsession/src/backend/consolekit.rs +++ b/libsession/libsession/src/backend/consolekit.rs @@ -25,6 +25,9 @@ trait ConsoleKitSession { #[zbus(name = "GetUnixUser")] fn get_unix_user(&self) -> zbus::Result; + #[zbus(name = "GetVTNr")] + fn get_vt_nr(&self) -> zbus::Result; + #[zbus(name = "IsActive")] fn is_active(&self) -> zbus::Result; @@ -57,6 +60,22 @@ impl super::Backend for ConsoleKit { crate::LibsessionBackend::ConsoleKit } + fn get_vt(&mut self, session_id: &str) -> super::Result> { + for path in self.manager.get_sessions()? { + let session = ConsoleKitSessionProxyBlocking::builder(&self.connection) + .path(path)? + .build()?; + if session.get_id()? == session_id { + return Ok(match session.get_vt_nr()? { + 0 => None, + vt => Some(vt), + }); + } + } + + Err(io::Error::new(io::ErrorKind::NotFound, "session not found").into()) + } + fn list_sessions(&mut self) -> super::Result> { self.logger.log(crate::LibsessionLogLevel::Debug, || { "consolekit: listing all sessions".to_string() diff --git a/libsession/libsession/src/backend/logind.rs b/libsession/libsession/src/backend/logind.rs index 59266a9..d64c8a0 100644 --- a/libsession/libsession/src/backend/logind.rs +++ b/libsession/libsession/src/backend/logind.rs @@ -40,6 +40,22 @@ impl super::Backend for Logind { crate::LibsessionBackend::Logind } + fn get_vt(&mut self, session_id: &str) -> super::Result> { + let session_id = ffi::CString::new(session_id) + .map_err(|err| io::Error::new(io::ErrorKind::InvalidInput, err))?; + let mut vt = 0; + let result = unsafe { login::sd_session_get_vt(session_id.as_ptr(), &raw mut vt) }; + + if result == -libc::ENXIO { + return Ok(None); + } + if result < 0 { + return Err(io::Error::from_raw_os_error(-result).into()); + } + + Ok((vt != 0).then_some(vt)) + } + fn list_sessions(&mut self) -> super::Result> { self.logger.log(crate::LibsessionLogLevel::Debug, || { "logind: listing all sessions".to_string() diff --git a/libsession/libsession/src/backend/mod.rs b/libsession/libsession/src/backend/mod.rs index 0745261..6e12ff5 100644 --- a/libsession/libsession/src/backend/mod.rs +++ b/libsession/libsession/src/backend/mod.rs @@ -46,6 +46,8 @@ pub trait Backend { fn get_monitor(&self) -> Option>; fn list_sessions(&mut self) -> Result>; + + fn get_vt(&mut self, session_id: &str) -> Result>; } pub trait BackendMonitor { diff --git a/libsession/libsession/src/backend/sessiond.rs b/libsession/libsession/src/backend/sessiond.rs index 9d35f4e..40c1bc2 100644 --- a/libsession/libsession/src/backend/sessiond.rs +++ b/libsession/libsession/src/backend/sessiond.rs @@ -27,6 +27,24 @@ impl super::Backend for Sessiond { crate::LibsessionBackend::Sessiond } + fn get_vt(&mut self, session_id: &str) -> super::Result> { + self.logger.log(crate::LibsessionLogLevel::Debug, || { + format!("sessiond: looking up vt for session ") + }); + + if self.monitor.has_changed()? { + self.state.try_read()?; + } + + let session = self + .state + .sessions + .get(session_id) + .ok_or_else(|| io::Error::new(io::ErrorKind::NotFound, "session not found"))?; + + Ok(session.vt.map(|vt| vt.get())) + } + fn list_sessions(&mut self) -> super::Result> { self.logger.log(crate::LibsessionLogLevel::Debug, || { "sessiond: listing all sessions".to_string() diff --git a/libsession/libsession/src/lib.rs b/libsession/libsession/src/lib.rs index ab5566f..15fb2fd 100644 --- a/libsession/libsession/src/lib.rs +++ b/libsession/libsession/src/lib.rs @@ -464,6 +464,28 @@ pub unsafe extern "C" fn libsession_session_is_local( LibsessionStatus::Ok } +#[unsafe(no_mangle)] +pub unsafe extern "C" fn libsession_session_get_vt( + ctx: *mut LibsessionContext, + session: *const LibsessionSession, + ret_vt: *mut u32, +) -> LibsessionStatus { + if ctx.is_null() || session.is_null() || ret_vt.is_null() { + return LibsessionStatus::InvalidArgument; + } + + let libsession = unsafe { &mut *ctx.cast::() }; + let session = unsafe { &*session.cast::() }; + + match libsession.backend.get_vt(&session.0) { + Ok(Some(vt)) => unsafe { ret_vt.write(vt) }, + Ok(None) => {} + Err(error) => return LibsessionStatus::from(&error), + } + + LibsessionStatus::Ok +} + /// Copies a session ID into an allocated, NUL-terminated C string. /// /// Free the returned string with `libsession_session_id_free`.