1mod backend;
2mod logging;
3
4#[cfg(not(any(feature = "sessiond", feature = "logind", feature = "consolekit")))]
5compile_error!(
6 "libsession requires at least one backend feature: `sessiond`, `logind`, or `consolekit`"
7);
8
9#[cfg(feature = "consolekit")]
10use backend::consolekit;
11#[cfg(feature = "logind")]
12use backend::logind;
13#[cfg(feature = "sessiond")]
14use backend::sessiond;
15use std::{env, ffi, process, ptr, rc};
16
17struct Libsession {
18 backend: Box<dyn backend::Backend>,
19 logger: rc::Rc<logging::Logger>,
20}
21
22#[allow(non_camel_case_types)]
23#[repr(C)]
24pub struct libsession_context {
25 _private: [u8; 0],
26}
27
28#[repr(C)]
29pub enum LibsessionStatus {
30 Ok = 0,
31 Failed = 1,
32}
33
34#[repr(C)]
35#[derive(Debug, Clone, Copy, PartialEq, Eq)]
36pub enum LibsessionUserState {
37 Offline = 0,
38 Lingering = 1,
39 Online = 2,
40 Active = 3,
41 Closing = 4,
42 Unknown = 5,
43}
44
45#[repr(C)]
46#[derive(PartialEq, Eq)]
47pub enum LibsessionSessionState {
48 Online = 0,
49 Active = 1,
50 Closing = 2,
51 Opening = 3,
52 Unkown = 4,
53}
54
55#[unsafe(no_mangle)]
56#[must_use]
57pub extern "C" fn libsession_new() -> *mut libsession_context {
58 let logger = rc::Rc::new(logging::Logger::default());
59 if let Ok(backend) = env::var("LIBSESSION_BACKEND") {
60 match backend.as_str() {
61 #[cfg(feature = "sessiond")]
62 "sessiond" => {
63 if let Some(sessiond) = sessiond::Sessiond::try_connect(rc::Rc::clone(&logger)) {
64 return Box::into_raw(Box::new(Libsession {
65 backend: Box::new(sessiond),
66 logger,
67 }))
68 .cast::<libsession_context>();
69 }
70 }
71 #[cfg(feature = "logind")]
72 "logind" => {
73 if let Some(logind) = logind::Logind::try_connect(rc::Rc::clone(&logger)) {
74 return Box::into_raw(Box::new(Libsession {
75 backend: Box::new(logind),
76 logger,
77 }))
78 .cast::<libsession_context>();
79 }
80 }
81 #[cfg(feature = "consolekit")]
82 "consolekit" => {
83 if let Some(consolekit) =
84 consolekit::ConsoleKit::try_connect(rc::Rc::clone(&logger))
85 {
86 return Box::into_raw(Box::new(Libsession {
87 backend: Box::new(consolekit),
88 logger,
89 }))
90 .cast::<libsession_context>();
91 }
92 }
93 _ => {}
94 }
95 }
96
97 #[cfg(feature = "sessiond")]
98 if let Some(sessiond) = sessiond::Sessiond::try_connect(rc::Rc::clone(&logger)) {
99 return Box::into_raw(Box::new(Libsession {
100 backend: Box::new(sessiond),
101 logger,
102 }))
103 .cast::<libsession_context>();
104 }
105 #[cfg(feature = "logind")]
106 if let Some(logind) = logind::Logind::try_connect(rc::Rc::clone(&logger)) {
107 return Box::into_raw(Box::new(Libsession {
108 backend: Box::new(logind),
109 logger,
110 }))
111 .cast::<libsession_context>();
112 }
113 #[cfg(feature = "consolekit")]
114 if let Some(consolekit) = consolekit::ConsoleKit::try_connect(rc::Rc::clone(&logger)) {
115 return Box::into_raw(Box::new(Libsession {
116 backend: Box::new(consolekit),
117 logger,
118 }))
119 .cast::<libsession_context>();
120 }
121
122 ptr::null_mut()
123}
124
125#[unsafe(no_mangle)]
126pub extern "C" fn libsession_free(ctx: *mut libsession_context) {
127 if ctx.is_null() {
128 return;
129 }
130
131 unsafe { drop(Box::from_raw(ctx.cast::<Libsession>())) }
132}
133
134#[unsafe(no_mangle)]
143pub unsafe extern "C" fn libsession_pid_get_session(
144 ctx: *mut libsession_context,
145 pid: libc::pid_t,
146 ret_session: *mut *mut ffi::c_char,
147) -> LibsessionStatus {
148 if ctx.is_null() || ret_session.is_null() {
149 return LibsessionStatus::Failed;
150 }
151
152 let libsession = unsafe { &mut *ctx.cast::<Libsession>() };
153
154 let Ok(pid) = pid.try_into() else {
155 return LibsessionStatus::Failed;
156 };
157 let Some(session_id) = libsession.backend.get_session_id(pid) else {
158 return LibsessionStatus::Failed;
159 };
160
161 let Ok(session_id) = ffi::CString::new(session_id) else {
162 return LibsessionStatus::Failed;
163 };
164
165 unsafe { ret_session.write(session_id.into_raw()) };
166 LibsessionStatus::Ok
167}
168
169#[unsafe(no_mangle)]
175pub unsafe extern "C" fn libsession_get_session(
176 ctx: *mut libsession_context,
177 ret_session: *mut *mut ffi::c_char,
178) -> LibsessionStatus {
179 let pid = process::id();
180 if let Ok(pid) = pid.try_into() {
181 return unsafe { libsession_pid_get_session(ctx, pid, ret_session) };
182 }
183
184 LibsessionStatus::Failed
185}
186
187#[unsafe(no_mangle)]
197pub unsafe extern "C" fn libsession_session_id_free(id: *mut ffi::c_char) {
198 if !id.is_null() {
199 unsafe { drop(ffi::CString::from_raw(id)) };
200 }
201}
202
203#[unsafe(no_mangle)]
204pub unsafe extern "C" fn libsession_pid_get_owner_uid(
207 ctx: *mut libsession_context,
208 pid: libc::pid_t,
209 ret_uid: *mut libc::uid_t,
210) -> LibsessionStatus {
211 if ctx.is_null() || pid < 0 || ret_uid.is_null() {
212 return LibsessionStatus::Failed;
213 }
214
215 let libsession = unsafe { &mut *ctx.cast::<Libsession>() };
216
217 if let Some(owner_uid) = libsession.backend.get_owner_uid(pid.cast_unsigned()) {
218 unsafe { ret_uid.write(owner_uid) };
219 return LibsessionStatus::Ok;
220 }
221
222 LibsessionStatus::Failed
223}
224
225#[unsafe(no_mangle)]
226pub unsafe extern "C" fn libsession_uid_get_display(
229 ctx: *mut libsession_context,
230 uid: libc::uid_t,
231 ret_session: *mut *mut ffi::c_char,
232) -> LibsessionStatus {
233 if ctx.is_null() || ret_session.is_null() {
234 return LibsessionStatus::Failed;
235 }
236
237 let libsession = unsafe { &mut *ctx.cast::<Libsession>() };
238
239 if let Some(session_id) = libsession.backend.get_display(uid)
240 && let Ok(session_id) = ffi::CString::new(session_id)
241 {
242 unsafe { ret_session.write(session_id.into_raw()) };
243 return LibsessionStatus::Ok;
244 }
245
246 LibsessionStatus::Failed
247}
248
249#[unsafe(no_mangle)]
250pub unsafe extern "C" fn libsession_uid_get_state(
253 ctx: *mut libsession_context,
254 uid: libc::uid_t,
255 ret_state: *mut LibsessionUserState,
256) -> LibsessionStatus {
257 if ctx.is_null() || ret_state.is_null() {
258 return LibsessionStatus::Failed;
259 }
260
261 let libsession = unsafe { &mut *ctx.cast::<Libsession>() };
262 let Some(state) = libsession.backend.get_state(uid) else {
263 return LibsessionStatus::Failed;
264 };
265 unsafe { ret_state.write(state) };
266
267 LibsessionStatus::Ok
268}
269
270#[unsafe(no_mangle)]
271pub unsafe extern "C" fn libsession_session_get_uid(
274 ctx: *mut libsession_context,
275 session: *const ffi::c_char,
276 ret_uid: *mut libc::uid_t,
277) -> LibsessionStatus {
278 if ctx.is_null() || session.is_null() || ret_uid.is_null() {
279 return LibsessionStatus::Failed;
280 }
281
282 let libsession = unsafe { &mut *ctx.cast::<Libsession>() };
283 if let Some(owner_uid) = libsession
284 .backend
285 .get_uid(unsafe { ffi::CStr::from_ptr(session) })
286 {
287 unsafe { ret_uid.write(owner_uid) };
288 return LibsessionStatus::Ok;
289 }
290
291 LibsessionStatus::Failed
292}
293
294#[unsafe(no_mangle)]
295pub unsafe extern "C" fn libsession_session_get_state(
298 ctx: *mut libsession_context,
299 session: *const ffi::c_char,
300 ret_state: *mut LibsessionSessionState,
301) -> LibsessionStatus {
302 if ctx.is_null() || session.is_null() || ret_state.is_null() {
303 return LibsessionStatus::Failed;
304 }
305
306 let libsession = unsafe { &mut *ctx.cast::<Libsession>() };
307 let Some(state) = libsession
308 .backend
309 .get_session_state(unsafe { ffi::CStr::from_ptr(session) })
310 else {
311 return LibsessionStatus::Failed;
312 };
313 unsafe { ret_state.write(state) };
314
315 LibsessionStatus::Ok
316}
317
318#[unsafe(no_mangle)]
319pub unsafe extern "C" fn libsession_session_is_active(
322 ctx: *mut libsession_context,
323 session: *const ffi::c_char,
324 ret_active: *mut bool,
325) -> LibsessionStatus {
326 if ctx.is_null() || session.is_null() || ret_active.is_null() {
327 return LibsessionStatus::Failed;
328 }
329
330 let libsession = unsafe { &mut *ctx.cast::<Libsession>() };
331 let Some(state) = libsession
332 .backend
333 .get_session_state(unsafe { ffi::CStr::from_ptr(session) })
334 else {
335 return LibsessionStatus::Failed;
336 };
337 unsafe { ret_active.write(state == LibsessionSessionState::Active) };
338
339 LibsessionStatus::Ok
340}
341
342#[unsafe(no_mangle)]
351pub unsafe extern "C" fn libsession_session_is_local(
352 ctx: *mut libsession_context,
353 session: *const ffi::c_char,
354 ret_local: *mut bool,
355) -> LibsessionStatus {
356 if ctx.is_null() || session.is_null() || ret_local.is_null() {
357 return LibsessionStatus::Failed;
358 }
359
360 let libsession = unsafe { &mut *ctx.cast::<Libsession>() };
361 let Some(local) = libsession
362 .backend
363 .is_local_session(unsafe { ffi::CStr::from_ptr(session) })
364 else {
365 return LibsessionStatus::Failed;
366 };
367 unsafe { ret_local.write(local) };
368
369 LibsessionStatus::Ok
370}
371
372#[allow(non_camel_case_types)]
373#[repr(C)]
374pub struct libsession_monitor {
375 _private: [u8; 0],
376}
377
378struct Monitor(Box<dyn backend::BackendMonitor>);
379
380#[unsafe(no_mangle)]
381pub extern "C" fn libsession_monitor_new(
382 ctx: *const libsession_context,
383) -> *mut libsession_monitor {
384 if ctx.is_null() {
385 return ptr::null_mut();
386 }
387 let libsession = unsafe { &*ctx.cast::<Libsession>() };
388 if let Some(monitor) = libsession.backend.get_monitor() {
389 return Box::into_raw(Box::new(Monitor(monitor))).cast::<libsession_monitor>();
390 }
391
392 ptr::null_mut()
393}
394
395#[unsafe(no_mangle)]
396pub extern "C" fn libsession_monitor_free(monitor: *mut libsession_monitor) {
397 if monitor.is_null() {
398 return;
399 }
400
401 unsafe { drop(Box::from_raw(monitor.cast::<Monitor>())) }
402}
403
404#[unsafe(no_mangle)]
405pub extern "C" fn libsession_monitor_flush(monitor: *mut libsession_monitor) {
406 if monitor.is_null() {
407 return;
408 }
409 let monitor = unsafe { &mut *monitor.cast::<Monitor>() };
410 monitor.0.flush();
411}
412
413#[unsafe(no_mangle)]
414pub extern "C" fn libsession_monitor_fd(monitor: *mut libsession_monitor) -> ffi::c_int {
415 if monitor.is_null() {
416 return -1;
417 }
418 let monitor = unsafe { &*monitor.cast::<Monitor>() };
419 monitor.0.fd()
420}
421
422pub type LibsessionLogFn = Option<unsafe extern "C" fn(LibsessionLogLevel, *const ffi::c_char)>;
423
424#[unsafe(no_mangle)]
433pub unsafe extern "C" fn libsession_set_log_fn(
434 ctx: *mut libsession_context,
435 callback: LibsessionLogFn,
436) -> LibsessionStatus {
437 if ctx.is_null() {
438 return LibsessionStatus::Failed;
439 }
440 let libsession = unsafe { &*ctx.cast::<Libsession>() };
441
442 libsession.logger.set_callback(callback);
443
444 LibsessionStatus::Ok
445}
446
447#[repr(C)]
448#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)]
449pub enum LibsessionLogLevel {
450 #[default]
451 Off = 0,
452 Error = 1,
453 Warn = 2,
454 Info = 3,
455 Debug = 4,
456 Trace = 5,
457}
458
459#[unsafe(no_mangle)]
466pub unsafe extern "C" fn libsession_set_log_level(
467 ctx: *mut libsession_context,
468 log_level: LibsessionLogLevel,
469) -> LibsessionStatus {
470 if ctx.is_null() {
471 return LibsessionStatus::Failed;
472 }
473 let libsession = unsafe { &*ctx.cast::<Libsession>() };
474
475 libsession.logger.set_level(log_level);
476
477 LibsessionStatus::Ok
478}
479
480#[unsafe(no_mangle)]
487pub unsafe extern "C" fn libsession_get_log_level(
488 ctx: *const libsession_context,
489) -> LibsessionLogLevel {
490 if ctx.is_null() {
491 return LibsessionLogLevel::Off;
492 }
493 let libsession = unsafe { &*ctx.cast::<Libsession>() };
494 libsession.logger.level()
495}