1extern crate libc;
2use crate::ipcon_error::IpconError;
3use crate::ipcon_msg::{IpconMsg, LibIpconMsg, IPCON_MAX_NAME_LEN, IPCON_MAX_PAYLOAD_LEN};
4use error_stack::{Report, Result, ResultExt};
5#[allow(unused)]
6use jlogger_tracing::{jdebug, jerror, jinfo, jwarn};
7use libc::{c_void, size_t};
8use nix::errno::Errno;
9use std::ffi::CString;
10use std::os::raw::{c_char, c_uchar};
11
12#[link(name = "ipcon")]
13extern "C" {
14 fn ipcon_create_handler(peer_name: *const c_char, flags: usize) -> *mut c_void;
15 fn ipcon_free_handler(handler: *mut c_void);
16 fn is_peer_present(handler: *mut c_void, peer: *const c_char) -> i32;
17 fn is_group_present(handler: *mut c_void, peer: *const c_char, group: *const c_char) -> i32;
18 fn ipcon_rcv(handler: *mut c_void, msg: &LibIpconMsg) -> i32;
19 fn ipcon_send_unicast(
20 handler: *mut c_void,
21 peer: *const c_char,
22 buf: *const c_uchar,
23 size: size_t,
24 ) -> i32;
25 fn ipcon_register_group(handler: *mut c_void, name: *const c_char) -> i32;
26 fn ipcon_unregister_group(handler: *mut c_void, name: *const c_char) -> i32;
27 fn ipcon_join_group(
28 handler: *mut c_void,
29 srv_name: *const c_char,
30 grp_name: *const c_char,
31 ) -> i32;
32 fn ipcon_leave_group(
33 handler: *mut c_void,
34 srv_name: *const c_char,
35 grp_name: *const c_char,
36 ) -> i32;
37 fn ipcon_send_multicast(
38 handler: *mut c_void,
39 name: *const c_char,
40 buf: *const c_uchar,
41 size: size_t,
42 sync: i32,
43 ) -> i32;
44 fn ipcon_rcv_timeout(
45 handler: *mut c_void,
46 im: &LibIpconMsg,
47 timeout: *const libc::timeval,
48 ) -> i32;
49 fn ipcon_get_read_fd(handler: *mut c_void) -> i32;
50 fn ipcon_get_write_fd(handler: *mut c_void) -> i32;
51 fn ipcon_get_ctrl_fd(handler: *mut c_void) -> i32;
52}
53
54pub struct Ipcon {
56 handler: usize,
57 name: Option<String>,
58}
59
60pub type IpconFlag = std::os::raw::c_ulong;
61pub const IPF_DISABLE_KEVENT_FILTER: IpconFlag = 0x1 << 0;
62pub const IPF_RCV_IF: IpconFlag = 0x1 << 1;
63pub const IPF_SND_IF: IpconFlag = 0x1 << 2;
64pub const IPF_DEFAULT: IpconFlag = IPF_RCV_IF | IPF_SND_IF;
65pub const IPCON_KERNEL_NAME: &str = "ipcon";
66pub const IPCON_KERNEL_GROUP_NAME: &str = "ipcon_kevent";
67
68fn errno_to_error(i: i32) -> IpconError {
69 let eno = Errno::from_i32(i.abs());
70 match eno {
71 Errno::ETIMEDOUT => IpconError::SysErrorTimeOut,
72 Errno::EINVAL => IpconError::SysErrorInvalidValue,
73 Errno::EPERM => IpconError::SysErrorPermission,
74 Errno::ENOENT => IpconError::SystemErrorNotExist,
75 _ => IpconError::SystemErrorOther,
76 }
77}
78
79pub fn valid_name(name: &str) -> Result<(), IpconError> {
80 let mut error_str = None;
81
82 if name.is_empty() {
83 error_str = Some("Name is null".to_owned());
84 }
85
86 if name.len() > IPCON_MAX_NAME_LEN {
87 error_str = Some(format!(
88 "Name is too long {} > {}",
89 name.len(),
90 IPCON_MAX_NAME_LEN
91 ));
92 }
93
94 if name.trim() != name {
95 error_str = Some("Name has blank character".to_owned());
96 }
97
98 if let Some(err_str) = error_str {
99 Err(Report::new(IpconError::InvalidName)).attach_printable(err_str)
100 } else {
101 Ok(())
102 }
103}
104
105impl Drop for Ipcon {
106 fn drop(&mut self) {
107 unsafe {
108 ipcon_free_handler(Ipcon::to_handler(self.handler));
109 }
110 }
111}
112
113impl Ipcon {
114 pub fn to_handler(u: usize) -> *mut c_void {
115 u as *mut c_void
116 }
117
118 pub unsafe fn from_handler(h: *mut c_void) -> usize {
120 h as usize
121 }
122
123 pub fn new(peer_name: Option<&str>, flag: Option<IpconFlag>) -> Result<Ipcon, IpconError> {
139 let handler: *mut c_void;
140 let mut flg = 0_usize;
141 let mut name = None;
142
143 if let Some(a) = flag {
144 flg = a as usize;
145 }
146
147 let pname = match peer_name {
148 Some(a) => {
149 valid_name(a).attach_printable(format!("Invalid peer name: {}", a))?;
150 name = Some(a.to_string());
151 CString::new(a)
152 .map_err(|_| Report::new(IpconError::InvalidName))?
153 .into_raw()
154 }
155 None => std::ptr::null(),
156 };
157
158 unsafe {
159 handler = ipcon_create_handler(pname as *const c_char, flg as usize);
160
161 if !pname.is_null() {
162 let _ = CString::from_raw(pname as *mut c_char);
164 }
165 }
166 if handler.is_null() {
167 Err(Report::new(IpconError::SystemErrorOther)).attach_printable(format!(
168 "Failed to create ipcon handler for {}, peer name already used?",
169 name.as_deref().unwrap_or("Anon")
170 ))
171 } else {
172 Ok(Ipcon {
173 handler: unsafe { Ipcon::from_handler(handler) },
174 name,
175 })
176 }
177 }
178
179 pub fn get_read_fd(&self) -> Result<i32, IpconError> {
181 unsafe {
182 let fd = ipcon_get_read_fd(Ipcon::to_handler(self.handler));
183 if fd < 0 {
184 Err(Report::new(errno_to_error(fd))).attach_printable(format!(
185 "ipcon_get_read_fd() {} get read fd failed: {}",
186 self.name.as_deref().unwrap_or("Anon"),
187 fd
188 ))
189 } else {
190 Ok(fd)
191 }
192 }
193 }
194
195 pub fn get_write_fd(&self) -> Result<i32, IpconError> {
197 unsafe {
198 let fd = ipcon_get_write_fd(Ipcon::to_handler(self.handler));
199 if fd < 0 {
200 Err(Report::new(errno_to_error(fd))).attach_printable(format!(
201 "ipcon_get_write_fd() {} get write fd failed: {}",
202 self.name.as_deref().unwrap_or("Anon"),
203 fd
204 ))
205 } else {
206 Ok(fd)
207 }
208 }
209 }
210
211 pub fn get_ctrl_fd(&self) -> Result<i32, IpconError> {
213 unsafe {
214 let fd = ipcon_get_ctrl_fd(Ipcon::to_handler(self.handler));
215 if fd < 0 {
216 Err(Report::new(errno_to_error(fd))).attach_printable(format!(
217 "ipcon_get_ctrl_fd() {} get ctrl fd failed: {}",
218 self.name.as_deref().unwrap_or("Anon"),
219 fd
220 ))
221 } else {
222 Ok(fd)
223 }
224 }
225 }
226
227 pub fn is_peer_present(&self, peer: &str) -> bool {
229 let mut present = false;
230 let p = match CString::new(peer) {
231 Ok(a) => a,
232 Err(_) => return false,
233 };
234
235 unsafe {
236 let ptr = p.into_raw();
237 let ret = is_peer_present(Ipcon::to_handler(self.handler), ptr as *const c_char);
238 if ret != 0 {
239 present = true;
240 }
241
242 let _ = CString::from_raw(ptr);
243 }
244
245 present
246 }
247
248 pub fn is_group_present(&self, peer: &str, group: &str) -> bool {
250 let mut present = false;
251 let p = match CString::new(peer) {
252 Ok(a) => a,
253 Err(_) => return false,
254 };
255
256 let g = match CString::new(group) {
257 Ok(a) => a,
258 Err(_) => return false,
259 };
260
261 unsafe {
262 let ptr = p.into_raw();
263 let pgtr = g.into_raw();
264 let ret = is_group_present(
265 Ipcon::to_handler(self.handler),
266 ptr as *const c_char,
267 pgtr as *const c_char,
268 );
269 let _ = CString::from_raw(ptr);
270 let _ = CString::from_raw(pgtr);
271
272 if ret != 0 {
273 present = true;
274 }
275 }
276
277 present
278 }
279
280 pub fn receive_msg(&self) -> Result<IpconMsg, IpconError> {
283 let lmsg = LibIpconMsg::new();
284
285 unsafe {
286 let ret = ipcon_rcv(Ipcon::to_handler(self.handler), &lmsg);
287 if ret < 0 {
288 return Err(Report::new(errno_to_error(ret))).attach_printable(format!(
289 "ipcon_rcv() {} receive message failed: {}",
290 self.name.as_deref().unwrap_or("Anon"),
291 ret
292 ));
293 }
294 }
295
296 lmsg.into()
297 }
298
299 pub fn send_unicast_msg(&self, peer: &str, buf: &[u8]) -> Result<(), IpconError> {
302 self.send_unicast_msg_by_ref(peer, buf)
303 }
304
305 pub fn send_unicast_msg_by_ref(&self, peer: &str, buf: &[u8]) -> Result<(), IpconError> {
308 valid_name(peer).attach_printable(format!("Invalid peer name: {}", peer))?;
309
310 if buf.len() > IPCON_MAX_PAYLOAD_LEN {
311 return Err(Report::new(IpconError::InvalidData)).attach_printable(format!(
312 "Buffer length is to large {} > {}",
313 buf.len(),
314 IPCON_MAX_PAYLOAD_LEN
315 ));
316 }
317
318 let pname = CString::new(peer).map_err(|_| Report::new(IpconError::InvalidData))?;
319
320 unsafe {
321 let ptr = pname.into_raw();
322 let ret = ipcon_send_unicast(
323 Ipcon::to_handler(self.handler),
324 ptr as *const c_char,
325 buf.as_ptr(),
326 buf.len() as size_t,
327 );
328
329 let _ = CString::from_raw(ptr);
330
331 if ret < 0 {
332 return Err(Report::new(errno_to_error(ret))).attach_printable(format!(
333 "send_unicast_msg() {} send message to peer `{}` failed: {}",
334 self.name.as_deref().unwrap_or("Anon"),
335 peer,
336 ret
337 ));
338 }
339 }
340
341 Ok(())
342 }
343
344 pub fn register_group(&self, group: &str) -> Result<(), IpconError> {
346 valid_name(group).attach_printable("register_group error: invalid group name")?;
347
348 let g = CString::new(group).map_err(|_| Report::new(IpconError::InvalidName))?;
349
350 unsafe {
351 let ptr = g.into_raw();
352 let ret = ipcon_register_group(Ipcon::to_handler(self.handler), ptr as *const c_char);
353 let _ = CString::from_raw(ptr);
354 if ret < 0 {
355 return Err(Report::new(errno_to_error(ret))).attach_printable(format!(
356 "ipcon_register_group() {} register `{}` failed: {}",
357 self.name.as_deref().unwrap_or("Anon"),
358 group,
359 ret
360 ));
361 }
362 }
363
364 Ok(())
365 }
366
367 pub fn unregister_group(&self, group: &str) -> Result<(), IpconError> {
369 valid_name(group).attach_printable(format!("Invalid group name: {}", group))?;
370
371 let g = CString::new(group).map_err(|_| Report::new(IpconError::InvalidName))?;
372
373 unsafe {
374 let ptr = g.into_raw();
375 let ret = ipcon_unregister_group(Ipcon::to_handler(self.handler), ptr as *const c_char);
376 let _ = CString::from_raw(ptr);
377 if ret < 0 {
378 return Err(Report::new(errno_to_error(ret))).attach_printable(format!(
379 "ipcon_unregister_group() {} unregister `{}` failed: {}",
380 self.name.as_deref().unwrap_or("Anon"),
381 group,
382 ret
383 ));
384 }
385 }
386
387 Ok(())
388 }
389
390 pub fn join_group(&self, peer: &str, group: &str) -> Result<(), IpconError> {
392 valid_name(peer).attach_printable(format!("Invalid peer name: {}", peer))?;
393 valid_name(group).attach_printable(format!("Invalid group name: {}", group))?;
394
395 let p = CString::new(peer).map_err(|_| Report::new(IpconError::InvalidName))?;
396 let g = CString::new(group).map_err(|_| Report::new(IpconError::InvalidName))?;
397
398 unsafe {
399 let ptr = p.into_raw();
400 let pgtr = g.into_raw();
401 let ret = ipcon_join_group(
402 Ipcon::to_handler(self.handler),
403 ptr as *const c_char,
404 pgtr as *const c_char,
405 );
406 let _ = CString::from_raw(ptr);
407 let _ = CString::from_raw(pgtr);
408 if ret < 0 {
409 return Err(Report::new(errno_to_error(ret))).attach_printable(format!(
410 "ipcon_join_group() {} join `{}@{}` failed: {}",
411 self.name.as_deref().unwrap_or("Anon"),
412 group,
413 peer,
414 ret
415 ));
416 }
417 }
418
419 Ok(())
420 }
421
422 pub fn leave_group(&self, peer: &str, group: &str) -> Result<(), IpconError> {
424 valid_name(peer).attach_printable(format!("Invalid peer name: {}", peer))?;
425 valid_name(group).attach_printable(format!("Invalid group name: {}", group))?;
426
427 let p = CString::new(peer).map_err(|_| Report::new(IpconError::InvalidName))?;
428 let g = CString::new(group).map_err(|_| Report::new(IpconError::InvalidName))?;
429
430 unsafe {
431 let ptr = p.into_raw();
432 let pgtr = g.into_raw();
433 let ret = ipcon_leave_group(
434 Ipcon::to_handler(self.handler),
435 ptr as *const c_char,
436 pgtr as *const c_char,
437 );
438 let _ = CString::from_raw(ptr);
439 let _ = CString::from_raw(pgtr);
440
441 if ret < 0 {
442 return Err(Report::new(errno_to_error(ret))).attach_printable(format!(
443 "ipcon_leave_group() {} leave `{}@{}` failed: {}",
444 self.name.as_deref().unwrap_or("Anon"),
445 group,
446 peer,
447 ret
448 ));
449 }
450 }
451
452 Ok(())
453 }
454
455 pub fn send_multicast(&self, group: &str, buf: &[u8], sync: bool) -> Result<(), IpconError> {
457 self.send_multicast_by_ref(group, buf, sync)
458 }
459
460 pub fn send_multicast_by_ref(
462 &self,
463 group: &str,
464 buf: &[u8],
465 sync: bool,
466 ) -> Result<(), IpconError> {
467 valid_name(group).attach_printable(format!("Invalid group name: {}", group))?;
468
469 if buf.len() > IPCON_MAX_PAYLOAD_LEN {
470 return Err(Report::new(IpconError::InvalidData)).attach_printable(format!(
471 "Buffer length is too large {} > {}",
472 buf.len(),
473 IPCON_MAX_PAYLOAD_LEN,
474 ));
475 }
476
477 let g = CString::new(group).map_err(|_| Report::new(IpconError::InvalidName))?;
478
479 let mut s: i32 = 0;
480 if sync {
481 s = 1;
482 }
483
484 unsafe {
485 let pgtr = g.into_raw();
486 let ret = ipcon_send_multicast(
487 Ipcon::to_handler(self.handler),
488 pgtr as *const c_char,
489 buf.as_ptr(),
490 buf.len() as size_t,
491 s,
492 );
493 let _ = CString::from_raw(pgtr);
494
495 if ret < 0 {
496 return Err(Report::new(errno_to_error(ret))).attach_printable(format!(
497 "ipcon_send_multicast() to `{}@{}` failed: {}",
498 group,
499 self.name.as_deref().unwrap_or("Anon"),
500 ret
501 ));
502 }
503 }
504
505 Ok(())
506 }
507
508 pub fn receive_msg_timeout(&self, tv_sec: u32, tv_usec: u32) -> Result<IpconMsg, IpconError> {
512 let lmsg = LibIpconMsg::new();
513 let t = libc::timeval {
514 tv_sec: tv_sec as libc::time_t,
515 tv_usec: tv_usec as libc::suseconds_t,
516 };
517
518 unsafe {
519 let ret = ipcon_rcv_timeout(Ipcon::to_handler(self.handler), &lmsg, &t);
520 if ret < 0 {
521 return Err(Report::new(errno_to_error(ret))).attach_printable(format!(
522 "ipcon_rcv_timeout() {} receive message failed: {}",
523 self.name.as_deref().unwrap_or("Anon"),
524 ret
525 ));
526 }
527 }
528
529 lmsg.into()
530 }
531
532 pub fn receive_msg_nonblock(&self) -> Result<IpconMsg, IpconError> {
535 self.receive_msg_timeout(0, 0)
536 }
537}