1use std::borrow::Cow;
16use std::fmt;
17use std::ops::{BitAnd, Shl};
18use std::path::Path;
19use std::sync::LazyLock;
20
21use nix::libc;
22
23
24use crate::portable;
25
26use super::error::SyRes;
27use super::throw_error;
28
29bitflags! {
30 pub struct LogStat: libc::c_int
32 {
33 const LOG_PID = libc::LOG_PID;
35
36 const LOG_CONS = libc::LOG_CONS;
39
40 const LOG_ODELAY = libc::LOG_ODELAY;
44
45 const LOG_NDELAY = libc::LOG_NDELAY;
47
48 const LOG_NOWAIT = libc::LOG_NOWAIT;
51
52 const LOG_PERROR = 0x20;
54 }
55}
56
57#[cfg(feature = "build_sync")]
58impl LogStat
59{
60 #[inline]
61 pub(super)
62 fn send_to_stderr(&self, msg: &[Cow<'_, str>])
63 {
64 if self.intersects(LogStat::LOG_PERROR) == true
65 {
66 let stderr_lock = std::io::stderr().lock();
67 let newline = "\n";
68
69 let _ = send_to_fd(stderr_lock, msg, &newline);
70 }
71 }
72
73 #[inline]
74 pub(super)
75 fn send_to_syscons(&self, msg_payload: &[Cow<'_, str>])
76 {
77 use std::fs::File;
78 use std::os::unix::fs::OpenOptionsExt;
79
80 if self.intersects(LogStat::LOG_CONS)
81 {
82 let syscons =
83 File
84 ::options()
85 .create(false)
86 .read(false)
87 .write(true)
88 .custom_flags(libc::O_NONBLOCK | libc::O_CLOEXEC)
89 .open(*PATH_CONSOLE);
90
91 if let Ok(file) = syscons
92 {
93 let newline = "\n";
94 let _ = send_to_fd(file, msg_payload, newline);
95 }
96 }
97 }
98}
99
100bitflags! {
101 pub(crate) struct LogMask: libc::c_int
102 {
103 const LOG_FACMASK = libc::LOG_FACMASK;
104 const LOG_PRIMASK = libc::LOG_PRIMASK;
105 }
106}
107
108bitflags! {
109 pub struct Priority: libc::c_int
111 {
112 const LOG_EMERG = libc::LOG_EMERG;
114
115 const LOG_ALERT = libc::LOG_ALERT;
117
118 const LOG_CRIT = libc::LOG_CRIT;
120
121 const LOG_ERR = libc::LOG_ERR;
123
124 const LOG_WARNING = libc::LOG_WARNING;
126
127 const LOG_NOTICE = libc::LOG_NOTICE;
129
130 const LOG_INFO = libc::LOG_INFO;
132
133 const LOG_DEBUG = libc::LOG_DEBUG;
135 }
136}
137
138impl fmt::Display for Priority
139{
140 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
141 {
142 if self.contains(Self::LOG_DEBUG) == true
145 {
146 write!(f, "[DEBUG]")
147 }
148 else if self.contains(Self::LOG_INFO) == true
149 {
150 write!(f, "[INFO]")
151 }
152 else if self.contains(Self::LOG_NOTICE) == true
153 {
154 write!(f, "[NOTICE]")
155 }
156 else if self.contains(Self::LOG_WARNING) == true
157 {
158 write!(f, "[WARNING]")
159 }
160 else if self.contains(Self::LOG_ERR) == true
161 {
162 write!(f, "[ERR]")
163 }
164 else if self.contains(Self::LOG_CRIT) == true
165 {
166 write!(f, "[CRIT]")
167 }
168 else if self.contains(Self::LOG_ALERT) == true
169 {
170 write!(f, "[ALERT]")
171 }
172 else if self.contains(Self::LOG_EMERG) == true
173 {
174 write!(f, "[EMERG]")
175 }
176 else
177 {
178 write!(f, "[UNKNOWN]")
179 }
180 }
181}
182
183impl Priority
184{
185 pub(crate)
197 fn check_invalid_bits(&mut self) -> SyRes<()>
198 {
199
200 if (self.bits() & !(LogMask::LOG_PRIMASK | LogMask::LOG_FACMASK )) != 0
201 {
202 let pri_old = self.clone();
203
204 *self = unsafe { Self::from_bits_unchecked( self.bits() & (LogMask::LOG_PRIMASK | LogMask::LOG_FACMASK).bits() ) };
205
206 throw_error!("unknwon facility/priority: {:x}", pri_old);
207 }
208
209 return Ok(());
210 }
211
212 pub(crate)
213 fn set_facility(&mut self, f: LogFacility)
214 {
215 *self = unsafe { Self::from_bits_unchecked(self.bits | f.bits() )};
216 }
217}
218
219bitflags! {
220 pub struct LogFacility: libc::c_int
223 {
224 const LOG_KERN = libc::LOG_KERN;
226
227 const LOG_USER = libc::LOG_USER;
229
230 const LOG_MAIL = libc::LOG_MAIL;
232
233 const LOG_DAEMON = libc::LOG_DAEMON;
235
236 const LOG_AUTH = libc::LOG_AUTH;
238
239 const LOG_SYSLOG = libc::LOG_SYSLOG;
241
242 const LOG_LPR = libc::LOG_LPR;
244
245 const LOG_NEWS = libc::LOG_NEWS;
247
248 const LOG_UUCP = libc::LOG_UUCP;
250
251 const LOG_LOCAL0 = libc::LOG_LOCAL0;
253
254 const LOG_LOCAL1 = libc::LOG_LOCAL1;
256
257 const LOG_LOCAL2 = libc::LOG_LOCAL2;
259
260 const LOG_LOCAL3 = libc::LOG_LOCAL3;
262
263 const LOG_LOCAL4 = libc::LOG_LOCAL4;
265
266 const LOG_LOCAL5 = libc::LOG_LOCAL5;
268
269 const LOG_LOCAL6 = libc::LOG_LOCAL6;
271
272 const LOG_LOCAL7 = libc::LOG_LOCAL7;
274 }
275}
276
277pub const MAXHOSTNAMELEN: usize = 256;
279
280pub const LOG_FACMASK: i32 = 0x03f8;
282
283pub const MAXLINE: usize = 8192;
287
288pub const RFC3164_MAX_PAYLOAD_LEN: usize = 1024;
290
291#[cfg(all(feature = "udp_truncate_1024_bytes", feature = "udp_truncate_1440_bytes"))]
292compile_error!("either 'udp_truncate_1024_bytes' or 'udp_truncate_1440_bytes' should be enabled");
293
294#[cfg(feature = "udp_truncate_1024_bytes")]
296pub const RFC5424_UDP_MAX_PKT_LEN: usize = 1024;
297
298#[cfg(any(feature = "udp_truncate_1440_bytes", all(not(feature = "udp_truncate_1440_bytes"), not(feature = "udp_truncate_1024_bytes"))))]
299pub const RFC5424_UDP_MAX_PKT_LEN: usize = 2048;
300
301#[cfg(feature = "tcp_truncate_1024_bytes")]
302pub const RFC5424_TCP_MAX_PKT_LEN: usize = 1024;
303
304#[cfg(feature = "tcp_truncate_2048_bytes")]
305pub const RFC5424_TCP_MAX_PKT_LEN: usize = 2048;
306
307#[cfg(feature = "tcp_truncate_4096_bytes")]
308pub const RFC5424_TCP_MAX_PKT_LEN: usize = 4096;
309
310#[cfg(feature = "tcp_truncate_max_bytes")]
311pub const RFC5424_TCP_MAX_PKT_LEN: usize = MAXLINE;
312
313pub const RFC_MAX_APP_NAME: usize = 48;
315
316pub const IANA_PRIV_ENT_NUM: u64 = 32473;
318
319pub const NILVALUE: &'static str = "-";
321
322pub const ESC_CHAR_REPL: &'static str = "#000";
324
325pub const NILVALUE_B: &'static [u8] = b"-";
327
328pub const WSPACE: &'static str = " ";
330
331pub const OBRACE: &'static str = "[";
333
334pub const CBRACE: &'static str = "]";
336
337pub const QCHAR: &'static str = "\"";
339
340pub const ATSIGN: &'static str = "@";
342
343pub const EQSIGN: &'static str = "=";
345
346pub const NEXTLINE: &'static str = "\n";
347
348pub const PATH_LOG: &'static str = "/var/run/log";
350
351pub const PATH_LOG_PRIV: &'static str = "/var/run/logpriv";
353
354pub const PATH_OLDLOG: &'static str = "/dev/log";
356
357pub const PATH_OSX: &'static str = "/var/run/syslog";
359
360pub static PATH_CONSOLE: LazyLock<&Path> = LazyLock::new(||
368 {
369 Path::new("/dev/console")
370 }
371);
372
373pub static RFC5424_MAX_DGRAM: LazyLock<usize> = LazyLock::new(||
374 {
375 portable::get_local_dgram_maxdgram() as usize
376 }
377);
378
379
380
381#[macro_export]
392macro_rules! LOG_MASK
393{
394 ($($arg:tt)*) => (
395 (1 << $($arg)*)
396 )
397}
398
399#[macro_export]
409macro_rules! LOG_UPTO
410{
411 ($($arg:tt)*) => (
412 ((1 << (($($arg)*) + 1)) - 1)
413 )
414}
415
416pub
418fn get_internal_log() -> libc::c_int
419{
420 return
421 Priority::LOG_ERR.bits() |
422 (LogStat::LOG_CONS| LogStat::LOG_PERROR| LogStat::LOG_PID).bits();
423}
424
425impl Shl<Priority> for i32
426{
427 type Output = i32;
428
429 fn shl(self, rhs: Priority) -> i32
430 {
431 let lhs = self;
432 return lhs << rhs.bits();
433 }
434}
435
436impl BitAnd<Priority> for i32
437{
438 type Output = i32;
439
440 #[inline]
441 fn bitand(self, rhs: Priority) -> i32
442 {
443 return self & rhs.bits();
444 }
445}
446
447impl BitAnd<LogMask> for Priority
448{
449 type Output = Priority;
450
451 #[inline]
452 fn bitand(self, rhs: LogMask) -> Self::Output
453 {
454 return Self {bits: self.bits() & rhs.bits()};
455 }
456}
457
458impl BitAnd<LogMask> for LogFacility
459{
460 type Output = LogFacility;
461
462 #[inline]
463 fn bitand(self, rhs: LogMask) -> Self::Output
464 {
465 return Self {bits: self.bits() & rhs.bits()};
466 }
467}
468
469impl BitAnd<LogMask> for i32
470{
471 type Output = i32;
472
473 #[inline]
474 fn bitand(self, rhs: LogMask) -> i32
475 {
476 return self & rhs.bits();
477 }
478}
479
480#[cfg(feature = "build_sync")]
481pub(crate) mod sync_portion
482{
483 use std::borrow::Cow;
484 use std::io::Write;
485 use std::io::IoSlice;
486 use crate::error::SyRes;
487 use crate::map_error_os;
488
489 pub(crate)
500 fn send_to_fd<W>(mut file_fd: W, msg: &[Cow<'_, str>], newline: &str) -> SyRes<usize>
501 where W: Write
502 {
503 let mut iov_list: Vec<IoSlice<'_>> = Vec::with_capacity(msg.len() + 1);
504
505 msg.iter().for_each(|v| iov_list.push(IoSlice::new(v.as_bytes())));
506 iov_list.push(IoSlice::new(newline.as_bytes()));
507
508 return
509 file_fd
510 .write_vectored(&iov_list)
511 .map_err(|e|
512 map_error_os!(e, "send_to_fd() writev() failed")
513 );
514 }
515}
516
517#[cfg(feature = "build_sync")]
518pub(crate) use self::sync_portion::*;
519
520pub
530fn truncate(lt: &str) -> &str
531{
532 let ltt =
533 match lt.char_indices().nth(lt.len()-1)
534 {
535 None => lt,
536 Some((idx, _)) => <[..idx],
537 };
538 return ltt;
539}
540
541pub
565fn truncate_n<'t>(lt: &'t str, n: usize) -> &'t str
566{
567 if lt.as_bytes().len() <= n
568 {
569 return lt;
570 }
571
572 let mut nn: usize = 0;
573 let mut cc = lt.chars();
574 let mut ln: usize;
575
576 loop
577 {
578 match cc.next()
579 {
580 Some(r) =>
581 {
582 ln = r.len_utf8();
583 nn += ln;
584
585 if nn == n
586 {
587 return <[..nn];
588 }
589 else if nn > n
590 {
591 return <[..nn-ln];
592 }
593 },
594 None =>
595 return lt,
596 }
597 }
598}
599
600pub
609fn check_printable(a: &str) -> SyRes<()>
610{
611 if a.is_empty() == true
612 {
613 throw_error!("empty SD value");
614 }
615
616 for p in a.chars()
617 {
618 if p.is_ascii() == false || p.is_ascii_graphic() == false || p == '@' || p == '=' || p == ']' || p == '\"'
619 {
620 throw_error!("incorrect char: '{:X}' in SD value", p as u64);
621 }
622 }
623
624 return Ok(());
625}
626
627
628pub
629fn escape_chars(st: Cow<'static, str>) -> Cow<'static, str>
630{
631 let mut out = String::with_capacity(st.len());
632
633 for c in st.chars()
634 {
635 if c.is_control() == true
636 {
637 out.push_str(ESC_CHAR_REPL);
638 }
639 else if c == '\"' || c == '\\' || c == ']'
640 {
641 out.push('\\');
642 out.push(c);
643 }
644 else
645 {
646 out.push(c);
647 }
648 }
649
650 if st.len() == out.len()
651 {
652 return st;
653 }
654 else
655 {
656 return Cow::Owned(out);
657 }
658}
659
660
661#[cfg(test)]
662mod tests
663{
664 use std::borrow::Cow;
665
666 use super::*;
667
668 #[cfg(feature = "build_sync")]
669 #[test]
670 fn test_error_message()
671 {
672 let testmsg = Cow::Borrowed("this is test message!");
678 let testmsg2 = Cow::Borrowed(" this is test message 2!");
679 let newline = "\n";
680 let stderr_lock = std::io::stderr().lock();
681 let res = send_to_fd(stderr_lock, &[testmsg, testmsg2], &newline);
682
683 println!("res: {:?}", res);
684
685 assert_eq!(res.is_ok(), true, "err: {}", res.err().unwrap());
686
687 return;
688 }
689
690 #[test]
691 fn test_truncate()
692 {
693 let test = "cat\n";
694
695 let trunc = truncate(test);
696
697 assert_eq!("cat", trunc);
698 }
699
700 #[test]
701 fn test_priority_shl()
702 {
703 assert_eq!((1 << 5), (1 << Priority::LOG_NOTICE));
704 }
705
706 #[test]
707 fn test_truncate_n()
708 {
709 assert_eq!(truncate_n("abcde", 3), "abc");
710 assert_eq!(truncate_n("ボルテ", 4), "ボ");
711 assert_eq!(truncate_n("ボルテ", 5), "ボ");
712 assert_eq!(truncate_n("ボルテ", 6), "ボル");
713 assert_eq!(truncate_n("abcde", 0), "");
714 assert_eq!(truncate_n("abcde", 5), "abcde");
715 assert_eq!(truncate_n("abcde", 6), "abcde");
716 assert_eq!(truncate_n("ДАТА", 3), "Д");
717 assert_eq!(truncate_n("ДАТА", 4), "ДА");
718 assert_eq!(truncate_n("ДАТА", 1), "");
719 }
720
721}