1use std::borrow::Cow;
18use std::fmt;
19use std::ops::{BitAnd, Shl};
20use std::path::Path;
21use std::sync::LazyLock;
22
23use nix::libc;
24
25
26use crate::portable;
27
28use super::error::SyRes;
29use super::throw_error;
30
31bitflags! {
32 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
34 pub struct LogStat: libc::c_int
35 {
36 const LOG_PID = libc::LOG_PID;
38
39 const LOG_CONS = libc::LOG_CONS;
42
43 const LOG_ODELAY = libc::LOG_ODELAY;
47
48 const LOG_NDELAY = libc::LOG_NDELAY;
50
51 const LOG_NOWAIT = libc::LOG_NOWAIT;
54
55 const LOG_PERROR = 0x20;
57 }
58}
59
60#[cfg(feature = "build_sync")]
61impl LogStat
62{
63 #[inline]
64 pub(super)
65 fn send_to_stderr(&self, msg: &str)
66 {
67 if self.intersects(LogStat::LOG_PERROR) == true
68 {
69 let stderr_lock = std::io::stderr().lock();
70 let newline = "\n";
71
72 let _ = send_to_fd(stderr_lock, msg, &newline);
73 }
74 }
75
76 #[inline]
77 pub(super)
78 fn send_to_syscons(&self, msg_payload: &str)
79 {
80 use std::fs::File;
81 use std::os::unix::fs::OpenOptionsExt;
82
83 if self.intersects(LogStat::LOG_CONS)
84 {
85 let syscons =
86 File
87 ::options()
88 .create(false)
89 .read(false)
90 .write(true)
91 .custom_flags(libc::O_NONBLOCK | libc::O_CLOEXEC)
92 .open(*PATH_CONSOLE);
93
94 if let Ok(file) = syscons
95 {
96 let newline = "\n";
97 let _ = send_to_fd(file, msg_payload, newline);
98 }
99 }
100 }
101}
102
103bitflags! {
104 pub(crate) struct LogMask: libc::c_int
105 {
106 const LOG_FACMASK = libc::LOG_FACMASK;
107 const LOG_PRIMASK = libc::LOG_PRIMASK;
108 }
109}
110
111bitflags! {
112 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
114 pub struct Priority: libc::c_int
115 {
116 const LOG_EMERG = libc::LOG_EMERG;
118
119 const LOG_ALERT = libc::LOG_ALERT;
121
122 const LOG_CRIT = libc::LOG_CRIT;
124
125 const LOG_ERR = libc::LOG_ERR;
127
128 const LOG_WARNING = libc::LOG_WARNING;
130
131 const LOG_NOTICE = libc::LOG_NOTICE;
133
134 const LOG_INFO = libc::LOG_INFO;
136
137 const LOG_DEBUG = libc::LOG_DEBUG;
139 }
140}
141
142impl fmt::Display for Priority
143{
144 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
145 {
146 if self.contains(Self::LOG_DEBUG) == true
149 {
150 write!(f, "[DEBUG]")
151 }
152 else if self.contains(Self::LOG_INFO) == true
153 {
154 write!(f, "[INFO]")
155 }
156 else if self.contains(Self::LOG_NOTICE) == true
157 {
158 write!(f, "[NOTICE]")
159 }
160 else if self.contains(Self::LOG_WARNING) == true
161 {
162 write!(f, "[WARNING]")
163 }
164 else if self.contains(Self::LOG_ERR) == true
165 {
166 write!(f, "[ERR]")
167 }
168 else if self.contains(Self::LOG_CRIT) == true
169 {
170 write!(f, "[CRIT]")
171 }
172 else if self.contains(Self::LOG_ALERT) == true
173 {
174 write!(f, "[ALERT]")
175 }
176 else if self.contains(Self::LOG_EMERG) == true
177 {
178 write!(f, "[EMERG]")
179 }
180 else
181 {
182 write!(f, "[UNKNOWN]")
183 }
184 }
185}
186
187impl Priority
188{
189 pub(crate)
201 fn check_invalid_bits(&mut self) -> SyRes<()>
202 {
203
204 if (self.bits() & !(LogMask::LOG_PRIMASK | LogMask::LOG_FACMASK )) != 0
205 {
206 let pri_old = self.clone();
207
208 *self = Self::from_bits_retain(self.bits() & (LogMask::LOG_PRIMASK | LogMask::LOG_FACMASK).bits() );
209
210 throw_error!("unknwon facility/priority: {:x}", pri_old);
211 }
212
213 return Ok(());
214 }
215
216 pub(crate)
217 fn set_facility(&mut self, f: LogFacility)
218 {
219 *self = Self::from_bits_retain(self.bits() | f.bits() );
220 }
221}
222
223bitflags! {
224 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
227 pub struct LogFacility: libc::c_int
228 {
229 const LOG_KERN = libc::LOG_KERN;
231
232 const LOG_USER = libc::LOG_USER;
234
235 const LOG_MAIL = libc::LOG_MAIL;
237
238 const LOG_DAEMON = libc::LOG_DAEMON;
240
241 const LOG_AUTH = libc::LOG_AUTH;
243
244 const LOG_SYSLOG = libc::LOG_SYSLOG;
246
247 const LOG_LPR = libc::LOG_LPR;
249
250 const LOG_NEWS = libc::LOG_NEWS;
252
253 const LOG_UUCP = libc::LOG_UUCP;
255
256 const LOG_LOCAL0 = libc::LOG_LOCAL0;
258
259 const LOG_LOCAL1 = libc::LOG_LOCAL1;
261
262 const LOG_LOCAL2 = libc::LOG_LOCAL2;
264
265 const LOG_LOCAL3 = libc::LOG_LOCAL3;
267
268 const LOG_LOCAL4 = libc::LOG_LOCAL4;
270
271 const LOG_LOCAL5 = libc::LOG_LOCAL5;
273
274 const LOG_LOCAL6 = libc::LOG_LOCAL6;
276
277 const LOG_LOCAL7 = libc::LOG_LOCAL7;
279 }
280}
281
282pub const MAXHOSTNAMELEN: usize = 256;
284
285pub const LOG_FACMASK: i32 = 0x03f8;
287
288pub const MAXLINE: usize = 8192;
292
293pub const RFC3164_MAX_PAYLOAD_LEN: usize = 1024;
295
296#[cfg(all(feature = "udp_truncate_1024_bytes", feature = "udp_truncate_1440_bytes"))]
297compile_error!("either 'udp_truncate_1024_bytes' or 'udp_truncate_1440_bytes' should be enabled");
298
299#[cfg(feature = "udp_truncate_1024_bytes")]
301pub const RFC5424_UDP_MAX_PKT_LEN: usize = 1024;
302
303#[cfg(any(feature = "udp_truncate_1440_bytes", all(not(feature = "udp_truncate_1440_bytes"), not(feature = "udp_truncate_1024_bytes"))))]
304pub const RFC5424_UDP_MAX_PKT_LEN: usize = 2048;
305
306#[cfg(feature = "tcp_truncate_1024_bytes")]
307pub const RFC5424_TCP_MAX_PKT_LEN: usize = 1024;
308
309#[cfg(feature = "tcp_truncate_2048_bytes")]
310pub const RFC5424_TCP_MAX_PKT_LEN: usize = 2048;
311
312#[cfg(feature = "tcp_truncate_4096_bytes")]
313pub const RFC5424_TCP_MAX_PKT_LEN: usize = 4096;
314
315#[cfg(feature = "tcp_truncate_max_bytes")]
316pub const RFC5424_TCP_MAX_PKT_LEN: usize = MAXLINE;
317
318pub const RFC_MAX_APP_NAME: usize = 48;
320
321pub const IANA_PRIV_ENT_NUM: u64 = 32473;
323
324pub const NILVALUE: &'static str = "-";
326
327pub const ESC_CHAR_REPL: &'static str = "#000";
329
330pub const NILVALUE_B: &'static [u8] = b"-";
332
333pub const WSPACE: &'static str = " ";
335
336pub const OBRACE: &'static str = "[";
338
339pub const CBRACE: &'static str = "]";
341
342pub const CBRACE_SEM: &'static str = "]:";
344
345pub const QCHAR: &'static str = "\"";
347
348pub const ATSIGN: &'static str = "@";
350
351pub const EQSIGN: &'static str = "=";
353
354pub const NEXTLINE: &'static str = "\n";
355
356pub const PATH_LOG: &'static str = "/var/run/log";
358
359pub const PATH_LOG_PRIV: &'static str = "/var/run/logpriv";
361
362pub const PATH_OLDLOG: &'static str = "/dev/log";
364
365pub const PATH_OSX: &'static str = "/var/run/syslog";
367
368pub static PATH_CONSOLE: LazyLock<&Path> = LazyLock::new(||
376 {
377 Path::new("/dev/console")
378 }
379);
380
381pub static RFC5424_MAX_DGRAM: LazyLock<usize> = LazyLock::new(||
382 {
383 portable::get_local_dgram_maxdgram() as usize
384 }
385);
386
387
388
389#[macro_export]
400macro_rules! LOG_MASK
401{
402 ($($arg:tt)*) => (
403 (1 << $($arg)*)
404 )
405}
406
407#[macro_export]
417macro_rules! LOG_UPTO
418{
419 ($($arg:tt)*) => (
420 ((1 << (($($arg)*) + 1)) - 1)
421 )
422}
423
424pub
426fn get_internal_log() -> libc::c_int
427{
428 return
429 Priority::LOG_ERR.bits() |
430 (LogStat::LOG_CONS| LogStat::LOG_PERROR| LogStat::LOG_PID).bits();
431}
432
433impl Shl<Priority> for i32
434{
435 type Output = i32;
436
437 fn shl(self, rhs: Priority) -> i32
438 {
439 let lhs = self;
440 return lhs << rhs.bits();
441 }
442}
443
444impl BitAnd<Priority> for i32
445{
446 type Output = i32;
447
448 #[inline]
449 fn bitand(self, rhs: Priority) -> i32
450 {
451 return self & rhs.bits();
452 }
453}
454
455impl BitAnd<LogMask> for Priority
456{
457 type Output = Priority;
458
459 #[inline]
460 fn bitand(self, rhs: LogMask) -> Self::Output
461 {
462
463 return Self::from_bits_retain(self.bits() & rhs.bits());
464 }
465}
466
467impl BitAnd<LogMask> for LogFacility
468{
469 type Output = LogFacility;
470
471 #[inline]
472 fn bitand(self, rhs: LogMask) -> Self::Output
473 {
474 return Self::from_bits_retain(self.bits() & rhs.bits());
475 }
476}
477
478impl BitAnd<LogMask> for i32
479{
480 type Output = i32;
481
482 #[inline]
483 fn bitand(self, rhs: LogMask) -> i32
484 {
485 return self & rhs.bits();
486 }
487}
488
489#[cfg(feature = "build_sync")]
490pub(crate) mod sync_portion
491{
492 use std::io::Write;
493 use std::io::IoSlice;
494 use crate::error::SyRes;
495 use crate::map_error_os;
496
497 pub(crate)
508 fn send_to_fd<W>(mut file_fd: W, msg: &str, newline: &str) -> SyRes<usize>
509 where W: Write
510 {
511 return
512 file_fd
513 .write_vectored(
514 &[IoSlice::new(msg.as_bytes()), IoSlice::new(newline.as_bytes())]
515 )
516 .map_err(|e|
517 map_error_os!(e, "send_to_fd() writev() failed")
518 );
519 }
520}
521
522#[cfg(feature = "build_sync")]
523pub(crate) use self::sync_portion::*;
524
525pub
535fn truncate(lt: &str) -> &str
536{
537 let ltt =
538 match lt.char_indices().nth(lt.len()-1)
539 {
540 None => lt,
541 Some((idx, _)) => <[..idx],
542 };
543 return ltt;
544}
545
546pub
570fn truncate_n<'t>(lt: &'t str, n: usize) -> &'t str
571{
572 if lt.as_bytes().len() <= n
573 {
574 return lt;
575 }
576
577 let mut nn: usize = 0;
578 let mut cc = lt.chars();
579 let mut ln: usize;
580
581 loop
582 {
583 match cc.next()
584 {
585 Some(r) =>
586 {
587 ln = r.len_utf8();
588 nn += ln;
589
590 if nn == n
591 {
592 return <[..nn];
593 }
594 else if nn > n
595 {
596 return <[..nn-ln];
597 }
598 },
599 None =>
600 return lt,
601 }
602 }
603}
604
605pub
614fn check_printable(a: &str) -> SyRes<()>
615{
616 if a.is_empty() == true
617 {
618 throw_error!("empty SD value");
619 }
620
621 for p in a.chars()
622 {
623 if p.is_ascii() == false || p.is_ascii_graphic() == false || p == '@' || p == '=' || p == ']' || p == '\"'
624 {
625 throw_error!("incorrect char: '{:X}' in SD value", p as u64);
626 }
627 }
628
629 return Ok(());
630}
631
632
633pub
634fn escape_chars(st: Cow<'static, str>) -> Cow<'static, str>
635{
636 let mut out = String::with_capacity(st.len());
637
638 for c in st.chars()
639 {
640 if c.is_control() == true
641 {
642 out.push_str(ESC_CHAR_REPL);
643 }
644 else if c == '\"' || c == '\\' || c == ']'
645 {
646 out.push('\\');
647 out.push(c);
648 }
649 else
650 {
651 out.push(c);
652 }
653 }
654
655 if st.len() == out.len()
656 {
657 return st;
658 }
659 else
660 {
661 return Cow::Owned(out);
662 }
663}
664
665
666#[cfg(test)]
667mod tests
668{
669 use super::*;
670
671 #[cfg(feature = "build_sync")]
672 #[test]
673 fn test_error_message()
674 {
675 let testmsg = "this is test message!";
681 let newline = "\n";
682 let stderr_lock = std::io::stderr().lock();
683 let res = send_to_fd(stderr_lock, testmsg, &newline);
684
685 println!("res: {:?}", res);
686
687 assert_eq!(res.is_ok(), true, "err: {}", res.err().unwrap());
688
689 return;
690 }
691
692 #[test]
693 fn test_truncate()
694 {
695 let test = "cat\n";
696
697 let trunc = truncate(test);
698
699 assert_eq!("cat", trunc);
700 }
701
702 #[test]
703 fn test_priority_shl()
704 {
705 assert_eq!((1 << 5), (1 << Priority::LOG_NOTICE));
706 }
707
708 #[test]
709 fn test_truncate_n()
710 {
711 assert_eq!(truncate_n("abcde", 3), "abc");
712 assert_eq!(truncate_n("ボルテ", 4), "ボ");
713 assert_eq!(truncate_n("ボルテ", 5), "ボ");
714 assert_eq!(truncate_n("ボルテ", 6), "ボル");
715 assert_eq!(truncate_n("abcde", 0), "");
716 assert_eq!(truncate_n("abcde", 5), "abcde");
717 assert_eq!(truncate_n("abcde", 6), "abcde");
718 assert_eq!(truncate_n("ДАТА", 3), "Д");
719 assert_eq!(truncate_n("ДАТА", 4), "ДА");
720 assert_eq!(truncate_n("ДАТА", 1), "");
721 }
722
723}