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: &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: &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 CBRACE_SEM: &'static str = "]:";
339
340pub const QCHAR: &'static str = "\"";
342
343pub const ATSIGN: &'static str = "@";
345
346pub const EQSIGN: &'static str = "=";
348
349pub const NEXTLINE: &'static str = "\n";
350
351pub const PATH_LOG: &'static str = "/var/run/log";
353
354pub const PATH_LOG_PRIV: &'static str = "/var/run/logpriv";
356
357pub const PATH_OLDLOG: &'static str = "/dev/log";
359
360pub const PATH_OSX: &'static str = "/var/run/syslog";
362
363pub static PATH_CONSOLE: LazyLock<&Path> = LazyLock::new(||
371 {
372 Path::new("/dev/console")
373 }
374);
375
376pub static RFC5424_MAX_DGRAM: LazyLock<usize> = LazyLock::new(||
377 {
378 portable::get_local_dgram_maxdgram() as usize
379 }
380);
381
382
383
384#[macro_export]
395macro_rules! LOG_MASK
396{
397 ($($arg:tt)*) => (
398 (1 << $($arg)*)
399 )
400}
401
402#[macro_export]
412macro_rules! LOG_UPTO
413{
414 ($($arg:tt)*) => (
415 ((1 << (($($arg)*) + 1)) - 1)
416 )
417}
418
419pub
421fn get_internal_log() -> libc::c_int
422{
423 return
424 Priority::LOG_ERR.bits() |
425 (LogStat::LOG_CONS| LogStat::LOG_PERROR| LogStat::LOG_PID).bits();
426}
427
428impl Shl<Priority> for i32
429{
430 type Output = i32;
431
432 fn shl(self, rhs: Priority) -> i32
433 {
434 let lhs = self;
435 return lhs << rhs.bits();
436 }
437}
438
439impl BitAnd<Priority> for i32
440{
441 type Output = i32;
442
443 #[inline]
444 fn bitand(self, rhs: Priority) -> i32
445 {
446 return self & rhs.bits();
447 }
448}
449
450impl BitAnd<LogMask> for Priority
451{
452 type Output = Priority;
453
454 #[inline]
455 fn bitand(self, rhs: LogMask) -> Self::Output
456 {
457 return Self {bits: self.bits() & rhs.bits()};
458 }
459}
460
461impl BitAnd<LogMask> for LogFacility
462{
463 type Output = LogFacility;
464
465 #[inline]
466 fn bitand(self, rhs: LogMask) -> Self::Output
467 {
468 return Self {bits: self.bits() & rhs.bits()};
469 }
470}
471
472impl BitAnd<LogMask> for i32
473{
474 type Output = i32;
475
476 #[inline]
477 fn bitand(self, rhs: LogMask) -> i32
478 {
479 return self & rhs.bits();
480 }
481}
482
483#[cfg(feature = "build_sync")]
484pub(crate) mod sync_portion
485{
486 use std::io::Write;
487 use std::io::IoSlice;
488 use crate::error::SyRes;
489 use crate::map_error_os;
490
491 pub(crate)
502 fn send_to_fd<W>(mut file_fd: W, msg: &str, newline: &str) -> SyRes<usize>
503 where W: Write
504 {
505 return
506 file_fd
507 .write_vectored(
508 &[IoSlice::new(msg.as_bytes()), IoSlice::new(newline.as_bytes())]
509 )
510 .map_err(|e|
511 map_error_os!(e, "send_to_fd() writev() failed")
512 );
513 }
514}
515
516#[cfg(feature = "build_sync")]
517pub(crate) use self::sync_portion::*;
518
519pub
529fn truncate(lt: &str) -> &str
530{
531 let ltt =
532 match lt.char_indices().nth(lt.len()-1)
533 {
534 None => lt,
535 Some((idx, _)) => <[..idx],
536 };
537 return ltt;
538}
539
540pub
564fn truncate_n<'t>(lt: &'t str, n: usize) -> &'t str
565{
566 if lt.as_bytes().len() <= n
567 {
568 return lt;
569 }
570
571 let mut nn: usize = 0;
572 let mut cc = lt.chars();
573 let mut ln: usize;
574
575 loop
576 {
577 match cc.next()
578 {
579 Some(r) =>
580 {
581 ln = r.len_utf8();
582 nn += ln;
583
584 if nn == n
585 {
586 return <[..nn];
587 }
588 else if nn > n
589 {
590 return <[..nn-ln];
591 }
592 },
593 None =>
594 return lt,
595 }
596 }
597}
598
599pub
608fn check_printable(a: &str) -> SyRes<()>
609{
610 if a.is_empty() == true
611 {
612 throw_error!("empty SD value");
613 }
614
615 for p in a.chars()
616 {
617 if p.is_ascii() == false || p.is_ascii_graphic() == false || p == '@' || p == '=' || p == ']' || p == '\"'
618 {
619 throw_error!("incorrect char: '{:X}' in SD value", p as u64);
620 }
621 }
622
623 return Ok(());
624}
625
626
627pub
628fn escape_chars(st: Cow<'static, str>) -> Cow<'static, str>
629{
630 let mut out = String::with_capacity(st.len());
631
632 for c in st.chars()
633 {
634 if c.is_control() == true
635 {
636 out.push_str(ESC_CHAR_REPL);
637 }
638 else if c == '\"' || c == '\\' || c == ']'
639 {
640 out.push('\\');
641 out.push(c);
642 }
643 else
644 {
645 out.push(c);
646 }
647 }
648
649 if st.len() == out.len()
650 {
651 return st;
652 }
653 else
654 {
655 return Cow::Owned(out);
656 }
657}
658
659
660#[cfg(test)]
661mod tests
662{
663 use super::*;
664
665 #[cfg(feature = "build_sync")]
666 #[test]
667 fn test_error_message()
668 {
669 let testmsg = "this is test message!";
675 let newline = "\n";
676 let stderr_lock = std::io::stderr().lock();
677 let res = send_to_fd(stderr_lock, testmsg, &newline);
678
679 println!("res: {:?}", res);
680
681 assert_eq!(res.is_ok(), true, "err: {}", res.err().unwrap());
682
683 return;
684 }
685
686 #[test]
687 fn test_truncate()
688 {
689 let test = "cat\n";
690
691 let trunc = truncate(test);
692
693 assert_eq!("cat", trunc);
694 }
695
696 #[test]
697 fn test_priority_shl()
698 {
699 assert_eq!((1 << 5), (1 << Priority::LOG_NOTICE));
700 }
701
702 #[test]
703 fn test_truncate_n()
704 {
705 assert_eq!(truncate_n("abcde", 3), "abc");
706 assert_eq!(truncate_n("ボルテ", 4), "ボ");
707 assert_eq!(truncate_n("ボルテ", 5), "ボ");
708 assert_eq!(truncate_n("ボルテ", 6), "ボル");
709 assert_eq!(truncate_n("abcde", 0), "");
710 assert_eq!(truncate_n("abcde", 5), "abcde");
711 assert_eq!(truncate_n("abcde", 6), "abcde");
712 assert_eq!(truncate_n("ДАТА", 3), "Д");
713 assert_eq!(truncate_n("ДАТА", 4), "ДА");
714 assert_eq!(truncate_n("ДАТА", 1), "");
715 }
716
717}