1use std::{borrow::Cow, fmt, ops::{BitAnd, Shl}, path::Path, sync::LazyLock};
20
21use crate::{error::{SyRes, SyslogError}, map_error_code, portable, throw_error};
22
23#[cfg(target_family = "windows")]
24pub use self::common_eventlog_items::*;
25
26#[cfg(target_family = "unix")]
27pub use self::common_syslog_items::*;
28
29#[cfg(target_family = "windows")]
30pub mod common_eventlog_items
31{
32 use std::fmt;
33
34 use windows::Win32::System::EventLog::{EVENTLOG_ERROR_TYPE, EVENTLOG_INFORMATION_TYPE, EVENTLOG_SUCCESS, EVENTLOG_WARNING_TYPE, REPORT_EVENT_TYPE};
35
36 use crate::{error::SyRes, throw_error};
37
38 #[allow(nonstandard_style)]
39 #[repr(i32)]
40 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
41 pub enum Priority
42 {
43 LOG_EMERG = 0,
45
46 LOG_ALERT = 1,
48
49 LOG_CRIT = 2,
51
52 LOG_ERR = 3,
54
55 LOG_WARNING = 4,
57
58 LOG_NOTICE = 5,
60
61 LOG_INFO = 6,
63
64 LOG_DEBUG = 7,
66 }
67
68 impl fmt::Display for Priority
69 {
70 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
71 {
72 match self
75 {
76 Self::LOG_EMERG =>
77 write!(f, "[EMERG]"),
78 Self::LOG_ALERT =>
79 write!(f, "[ALERT]"),
80 Self::LOG_CRIT =>
81 write!(f, "[CRIT]"),
82 Self::LOG_ERR =>
83 write!(f, "[ERR]"),
84 Self::LOG_WARNING =>
85 write!(f, "[WARNING]"),
86 Self::LOG_NOTICE =>
87 write!(f, "[NOTICE]"),
88 Self::LOG_INFO =>
89 write!(f, "[INFO]"),
90 Self::LOG_DEBUG =>
91 write!(f, "[DEBUG]"),
92 }
93 }
94 }
95
96 impl From<Priority> for REPORT_EVENT_TYPE
97 {
98 fn from(value: Priority) -> Self
99 {
100 match value
101 {
102 Priority::LOG_EMERG | Priority::LOG_ALERT | Priority::LOG_CRIT =>
103 return EVENTLOG_WARNING_TYPE,
104 Priority::LOG_ERR =>
105 return EVENTLOG_ERROR_TYPE,
106 Priority::LOG_WARNING | Priority::LOG_NOTICE =>
107 return EVENTLOG_INFORMATION_TYPE,
108 Priority::LOG_INFO | Priority::LOG_DEBUG =>
109 return EVENTLOG_SUCCESS
110 }
111 }
112 }
113
114 impl Priority
115 {
116 }
144
145
146
147 bitflags! {
148 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
150 pub struct LogStat: i32
151 {
152 const LOG_PID = 1;
154
155 const LOG_CONS = 2;
158
159 const LOG_ODELAY = 0;
163
164 const LOG_NDELAY = 0;
166
167 const LOG_NOWAIT = 0;
170
171 const LOG_PERROR = 0x20;
173 }
174 }
175
176 #[cfg(feature = "build_sync")]
177 impl LogStat
178 {
179 #[inline]
180 pub(crate)
181 fn send_to_stderr(&self, msg: &str)
182 {
183 if self.intersects(LogStat::LOG_PERROR) == true
184 {
185 eprintln!("{}", msg);
186 }
187 }
188
189 #[inline]
190 pub(crate)
191 fn send_to_syscons(&self, msg_payload: &str)
192 {
193 if self.intersects(LogStat::LOG_CONS)
194 {
195 eprintln!("{}", msg_payload);
196 }
197 }
198 }
199
200 bitflags! {
201 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
204 pub struct LogFacility: i32
205 {
206 const LOG_KERN = 0;
208
209 const LOG_USER = 8;
211
212 const LOG_MAIL = 16;
214
215 const LOG_DAEMON = 24;
217
218 const LOG_AUTH = 32;
220
221 const LOG_SYSLOG = 40;
223
224 const LOG_LPR = 48;
226
227 const LOG_NEWS = 56;
229
230 const LOG_UUCP = 64;
232
233 const LOG_LOCAL0 = 128;
235
236 const LOG_LOCAL1 = 136;
238
239 const LOG_LOCAL2 = 144;
241
242 const LOG_LOCAL3 = 152;
244
245 const LOG_LOCAL4 = 160;
247
248 const LOG_LOCAL5 = 168;
250
251 const LOG_LOCAL6 = 176;
253
254 const LOG_LOCAL7 = 184;
256 }
257 }
258
259 impl LogFacility
260 {
261 pub
262 fn into_win_facility(self) -> u32
263 {
264 return self.bits() as u32 >> 3;
265 }
266 }
267
268 #[cfg(test)]
269 mod tests
270 {
271 use windows::Win32::System::EventLog::{EVENTLOG_ERROR_TYPE, EVENTLOG_INFORMATION_TYPE, EVENTLOG_SUCCESS, EVENTLOG_WARNING_TYPE, REPORT_EVENT_TYPE};
272
273 use crate::Priority;
274
275 #[test]
276 fn test_conversion_prio_to_ret()
277 {
278 assert_eq!(REPORT_EVENT_TYPE::from(Priority::LOG_EMERG), EVENTLOG_WARNING_TYPE);
279 assert_eq!(REPORT_EVENT_TYPE::from(Priority::LOG_ALERT), EVENTLOG_WARNING_TYPE);
280 assert_eq!(REPORT_EVENT_TYPE::from(Priority::LOG_CRIT), EVENTLOG_WARNING_TYPE);
281 assert_eq!(REPORT_EVENT_TYPE::from(Priority::LOG_ERR), EVENTLOG_ERROR_TYPE);
282 assert_eq!(REPORT_EVENT_TYPE::from(Priority::LOG_WARNING), EVENTLOG_INFORMATION_TYPE);
283 assert_eq!(REPORT_EVENT_TYPE::from(Priority::LOG_NOTICE), EVENTLOG_INFORMATION_TYPE);
284 assert_eq!(REPORT_EVENT_TYPE::from(Priority::LOG_INFO), EVENTLOG_SUCCESS);
285 assert_eq!(REPORT_EVENT_TYPE::from(Priority::LOG_DEBUG), EVENTLOG_SUCCESS);
286 }
287 }
288}
289
290#[cfg(target_family = "unix")]
291pub mod common_syslog_items
292{
293 use nix::libc;
294
295 use std::fmt;
296
297
298 bitflags! {
299 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
301 pub struct LogStat: libc::c_int
302 {
303 const LOG_PID = libc::LOG_PID;
305
306 const LOG_CONS = libc::LOG_CONS;
309
310 const LOG_ODELAY = libc::LOG_ODELAY;
314
315 const LOG_NDELAY = libc::LOG_NDELAY;
317
318 const LOG_NOWAIT = libc::LOG_NOWAIT;
321
322 const LOG_PERROR = 0x20;
324 }
325 }
326
327 #[cfg(feature = "build_sync")]
328 impl LogStat
329 {
330 #[inline]
331 pub(crate)
332 fn send_to_stderr(&self, msg: &str)
333 {
334 if self.intersects(LogStat::LOG_PERROR) == true
335 {
336 let stderr_lock = std::io::stderr().lock();
337 let newline = "\n";
338
339 let _ = send_to_fd(stderr_lock, msg, &newline);
340 }
341 }
342
343 #[inline]
344 pub(crate)
345 fn send_to_syscons(&self, msg_payload: &str)
346 {
347 use std::fs::File;
348 use std::os::unix::fs::OpenOptionsExt;
349
350 if self.intersects(LogStat::LOG_CONS)
351 {
352 use crate::PATH_CONSOLE;
353
354 let syscons =
355 File
356 ::options()
357 .create(false)
358 .read(false)
359 .write(true)
360 .custom_flags(libc::O_NONBLOCK | libc::O_CLOEXEC)
361 .open(*PATH_CONSOLE);
362
363 if let Ok(file) = syscons
364 {
365 let newline = "\n";
366 let _ = send_to_fd(file, msg_payload, newline);
367 }
368 }
369 }
370 }
371
372 #[allow(nonstandard_style)]
373 #[repr(i32)]
374 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
375 pub enum Priority
376 {
377 LOG_EMERG = libc::LOG_EMERG,
379
380 LOG_ALERT = libc::LOG_ALERT,
382
383 LOG_CRIT = libc::LOG_CRIT,
385
386 LOG_ERR = libc::LOG_ERR,
388
389 LOG_WARNING = libc::LOG_WARNING,
391
392 LOG_NOTICE = libc::LOG_NOTICE,
394
395 LOG_INFO = libc::LOG_INFO,
397
398 LOG_DEBUG = libc::LOG_DEBUG,
400 }
401
402 impl fmt::Display for Priority
403 {
404 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
405 {
406 match self
409 {
410 Self::LOG_EMERG =>
411 write!(f, "[EMERG]"),
412 Self::LOG_ALERT =>
413 write!(f, "[ALERT]"),
414 Self::LOG_CRIT =>
415 write!(f, "[CRIT]"),
416 Self::LOG_ERR =>
417 write!(f, "[ERR]"),
418 Self::LOG_WARNING =>
419 write!(f, "[WARNING]"),
420 Self::LOG_NOTICE =>
421 write!(f, "[NOTICE]"),
422 Self::LOG_INFO =>
423 write!(f, "[INFO]"),
424 Self::LOG_DEBUG =>
425 write!(f, "[DEBUG]"),
426 }
427 }
428 }
429
430
431 impl Priority
432 {
433
434 }
435
436 bitflags! {
437 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
440 pub struct LogFacility: libc::c_int
441 {
442 const LOG_KERN = libc::LOG_KERN;
444
445 const LOG_USER = libc::LOG_USER;
447
448 const LOG_MAIL = libc::LOG_MAIL;
450
451 const LOG_DAEMON = libc::LOG_DAEMON;
453
454 const LOG_AUTH = libc::LOG_AUTH;
456
457 const LOG_SYSLOG = libc::LOG_SYSLOG;
459
460 const LOG_LPR = libc::LOG_LPR;
462
463 const LOG_NEWS = libc::LOG_NEWS;
465
466 const LOG_UUCP = libc::LOG_UUCP;
468
469 const LOG_LOCAL0 = libc::LOG_LOCAL0;
471
472 const LOG_LOCAL1 = libc::LOG_LOCAL1;
474
475 const LOG_LOCAL2 = libc::LOG_LOCAL2;
477
478 const LOG_LOCAL3 = libc::LOG_LOCAL3;
480
481 const LOG_LOCAL4 = libc::LOG_LOCAL4;
483
484 const LOG_LOCAL5 = libc::LOG_LOCAL5;
486
487 const LOG_LOCAL6 = libc::LOG_LOCAL6;
489
490 const LOG_LOCAL7 = libc::LOG_LOCAL7;
492 }
493 }
494
495
496
497
498 pub const PATH_LOG: &'static str = "/var/run/log";
500
501 pub const PATH_LOG_PRIV: &'static str = "/var/run/logpriv";
503
504 pub const PATH_OLDLOG: &'static str = "/dev/log";
506
507 pub const PATH_OSX: &'static str = "/var/run/syslog";
509
510 #[cfg(feature = "build_sync")]
519 pub(crate) mod sync_portion
520 {
521 use std::io::Write;
522 use std::io::IoSlice;
523 use crate::error::SyRes;
524 use crate::map_error_os;
525
526 pub(crate)
537 fn send_to_fd<W>(mut file_fd: W, msg: &str, newline: &str) -> SyRes<usize>
538 where W: Write
539 {
540 return
541 file_fd
542 .write_vectored(
543 &[IoSlice::new(msg.as_bytes()), IoSlice::new(newline.as_bytes())]
544 )
545 .map_err(|e|
546 map_error_os!(e, "send_to_fd() writev() failed")
547 );
548 }
549 }
550
551 #[cfg(feature = "build_sync")]
552 pub(crate) use self::sync_portion::*;
553
554 #[cfg(test)]
555 mod tests
556 {
557 use super::*;
558
559 #[cfg(feature = "build_sync")]
560 #[test]
561 fn test_error_message()
562 {
563 let testmsg = "this is test message!";
569 let newline = "\n";
570 let stderr_lock = std::io::stderr().lock();
571 let res = send_to_fd(stderr_lock, testmsg, &newline);
572
573 println!("res: {:?}", res);
574
575 assert_eq!(res.is_ok(), true, "err: {}", res.err().unwrap());
576
577 return;
578 }
579
580 #[test]
581 fn test_priority_shl()
582 {
583 assert_eq!((1 << 5), (1 << Priority::LOG_NOTICE));
584 }
585 }
586}
587
588pub static PATH_CONSOLE: LazyLock<&Path> = LazyLock::new(||
590 {
591 Path::new("/dev/console")
592 }
593);
594
595pub static RFC5424_MAX_DGRAM: LazyLock<usize> = LazyLock::new(||
597 {
598 portable::get_local_dgram_maxdgram() as usize
599 }
600);
601
602
603pub const MAXHOSTNAMELEN: usize = 256;
605
606pub const LOG_FACMASK: i32 = 0x03f8;
608
609pub const MAXLINE: usize = 8192;
613
614pub const RFC3164_MAX_PAYLOAD_LEN: usize = 1024;
616
617pub const WINDOWS_EVENT_REPORT_MAX_PAYLOAD_LEN: usize = 31839;
619
620#[cfg(all(feature = "udp_truncate_1024_bytes", feature = "udp_truncate_1440_bytes"))]
621compile_error!("either 'udp_truncate_1024_bytes' or 'udp_truncate_1440_bytes' should be enabled");
622
623#[cfg(feature = "udp_truncate_1024_bytes")]
625pub const RFC5424_UDP_MAX_PKT_LEN: usize = 1024;
626
627#[cfg(any(feature = "udp_truncate_1440_bytes", all(not(feature = "udp_truncate_1440_bytes"), not(feature = "udp_truncate_1024_bytes"))))]
628pub const RFC5424_UDP_MAX_PKT_LEN: usize = 2048;
629
630#[cfg(feature = "tcp_truncate_1024_bytes")]
631pub const RFC5424_TCP_MAX_PKT_LEN: usize = 1024;
632
633#[cfg(feature = "tcp_truncate_2048_bytes")]
634pub const RFC5424_TCP_MAX_PKT_LEN: usize = 2048;
635
636#[cfg(feature = "tcp_truncate_4096_bytes")]
637pub const RFC5424_TCP_MAX_PKT_LEN: usize = 4096;
638
639#[cfg(feature = "tcp_truncate_max_bytes")]
640pub const RFC5424_TCP_MAX_PKT_LEN: usize = MAXLINE;
641
642pub const RFC_MAX_APP_NAME: usize = 48;
644
645pub const IANA_PRIV_ENT_NUM: u64 = 32473;
647
648pub const NILVALUE: &'static str = "-";
650
651pub const ESC_CHAR_REPL: &'static str = "#000";
653
654pub const NILVALUE_B: &'static [u8] = b"-";
656
657pub const WSPACE: &'static str = " ";
659
660pub const OBRACE: &'static str = "[";
662
663pub const CBRACE: &'static str = "]";
665
666pub const CBRACE_SEM: &'static str = "]:";
668
669pub const QCHAR: &'static str = "\"";
671
672pub const ATSIGN: &'static str = "@";
674
675pub const EQSIGN: &'static str = "=";
677
678pub const NEXTLINE: &'static str = "\n";
680
681bitflags! {
682 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
684 pub(crate) struct LogMask: i32
685 {
686 const LOG_FACMASK = 0x3f8;
688
689 const LOG_PRIMASK = 7;
691 }
692}
693
694#[derive(Debug, Clone, Copy, PartialEq, Eq)]
700pub struct SyslogMsgPriFac(i32);
701
702impl fmt::Display for SyslogMsgPriFac
703{
704 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
705 {
706 write!(f, "{}", self.0)
707 }
708}
709
710impl TryFrom<i32> for SyslogMsgPriFac
711{
712 type Error = SyslogError;
713
714 fn try_from(value: i32) -> Result<Self, Self::Error>
715 {
716 if (value & !(LogMask::LOG_PRIMASK | LogMask::LOG_FACMASK )) != 0
717 {
718 throw_error!("unknwon facility/priority: {:x}", value);
719 }
720
721 return Ok(Self(value));
722 }
723}
724
725impl TryFrom<&[u8]> for SyslogMsgPriFac
726{
727 type Error = SyslogError;
728
729 fn try_from(value: &[u8]) -> Result<Self, Self::Error>
730 {
731 let val =
732 str::from_utf8(value)
733 .map_or(
734 None,
735 |pri_str|
736 {
737 i32::from_str_radix(pri_str, 10)
738 .map_or(
739 None,
740 |val|
741 Some(val & LogMask::LOG_PRIMASK | val & LogMask::LOG_FACMASK.bits())
742 )
743 }
744 )
745 .ok_or(
746 map_error_code!(InternalError, "cannot convert to prio|facility")
747 )?;
748
749 if (val & !(LogMask::LOG_PRIMASK | LogMask::LOG_FACMASK )) != 0
750 {
751 throw_error!("unknwon facility/priority: {:x}", val);
752 }
753
754 return Ok(Self(val));
755 }
756}
757
758impl SyslogMsgPriFac
759{
760 pub(crate)
763 fn set_facility(p: Priority, f: LogFacility) -> Self
764 {
765 return Self( p as i32 | f.bits() );
766 }
767
768 pub
770 fn get_val(&self) -> i32
771 {
772 return self.0;
773 }
774
775 pub
777 fn get_priority(&self) -> Priority
778 {
779 return Priority::from(self.0 & LogMask::LOG_PRIMASK);
780 }
781
782 pub
784 fn get_log_facility(&self) -> LogFacility
785 {
786 return LogFacility::from_bits_retain(self.0 & LogMask::LOG_FACMASK);
787 }
788}
789
790impl From<i32> for Priority
791{
792 fn from(value: i32) -> Self
793 {
794 if value == Priority::LOG_ALERT as i32
795 {
796 return Priority::LOG_ALERT;
797 }
798 else if value == Priority::LOG_CRIT as i32
799 {
800 return Priority::LOG_CRIT;
801 }
802 else if value == Priority::LOG_DEBUG as i32
803 {
804 return Priority::LOG_DEBUG;
805 }
806 else if value == Priority::LOG_EMERG as i32
807 {
808 return Priority::LOG_EMERG;
809 }
810 else if value == Priority::LOG_ERR as i32
811 {
812 return Priority::LOG_ERR;
813 }
814 else if value == Priority::LOG_INFO as i32
815 {
816 return Priority::LOG_INFO;
817 }
818 else if value == Priority::LOG_NOTICE as i32
819 {
820 return Priority::LOG_NOTICE;
821 }
822 else
823 {
824 return Priority::LOG_WARNING;
825 }
826 }
827}
828
829#[macro_export]
840macro_rules! LOG_MASK
841{
842 ($arg:expr) => (
843 (1 << ($arg))
844 )
845}
846
847#[macro_export]
857macro_rules! LOG_UPTO
858{
859 ($arg:expr) => (
860 ((1 << (($arg) + 1)) - 1)
861 )
862}
863
864impl Shl<Priority> for i32
865{
866 type Output = i32;
867
868 fn shl(self, rhs: Priority) -> i32
869 {
870 let lhs = self;
871 return lhs << rhs as i32;
872 }
873}
874
875impl BitAnd<Priority> for i32
876{
877 type Output = i32;
878
879 #[inline]
880 fn bitand(self, rhs: Priority) -> i32
881 {
882 return self & rhs as i32;
883 }
884}
885
886impl BitAnd<LogMask> for Priority
887{
888 type Output = i32;
889
890 #[inline]
891 fn bitand(self, rhs: LogMask) -> i32
892 {
893 return self as i32 & rhs.bits();
894 }
895}
896
897impl BitAnd<LogMask> for LogFacility
898{
899 type Output = LogFacility;
900
901 #[inline]
902 fn bitand(self, rhs: LogMask) -> Self::Output
903 {
904 return Self::from_bits_retain(self.bits() & rhs.bits());
905 }
906}
907
908impl BitAnd<LogMask> for i32
909{
910 type Output = i32;
911
912 #[inline]
913 fn bitand(self, rhs: LogMask) -> i32
914 {
915 return self & rhs.bits();
916 }
917}
918
919pub
929fn truncate(lt: &str) -> &str
930{
931 let ltt =
932 match lt.char_indices().nth(lt.len()-1)
933 {
934 None => lt,
935 Some((idx, _)) => <[..idx],
936 };
937 return ltt;
938}
939
940pub
964fn truncate_n<'t>(lt: &'t str, n: usize) -> &'t str
965{
966 if lt.as_bytes().len() <= n
967 {
968 return lt;
969 }
970
971 let mut nn: usize = 0;
972 let mut cc = lt.chars();
973 let mut ln: usize;
974
975 loop
976 {
977 match cc.next()
978 {
979 Some(r) =>
980 {
981 ln = r.len_utf8();
982 nn += ln;
983
984 if nn == n
985 {
986 return <[..nn];
987 }
988 else if nn > n
989 {
990 return <[..nn-ln];
991 }
992 },
993 None =>
994 return lt,
995 }
996 }
997}
998
999pub
1008fn check_printable(a: &str) -> SyRes<()>
1009{
1010 if a.is_empty() == true
1011 {
1012 throw_error!("empty SD value");
1013 }
1014
1015 for p in a.chars()
1016 {
1017 if p.is_ascii() == false || p.is_ascii_graphic() == false || p == '@' || p == '=' || p == ']' || p == '\"'
1018 {
1019 throw_error!("incorrect char: '{:X}' in SD value", p as u64);
1020 }
1021 }
1022
1023 return Ok(());
1024}
1025
1026
1027pub
1028fn escape_chars(st: Cow<'static, str>) -> Cow<'static, str>
1029{
1030 let mut out = String::with_capacity(st.len());
1031
1032 for c in st.chars()
1033 {
1034 if c.is_control() == true
1035 {
1036 out.push_str(ESC_CHAR_REPL);
1037 }
1038 else if c == '\"' || c == '\\' || c == ']'
1039 {
1040 out.push('\\');
1041 out.push(c);
1042 }
1043 else
1044 {
1045 out.push(c);
1046 }
1047 }
1048
1049 if st.len() == out.len()
1050 {
1051 return st;
1052 }
1053 else
1054 {
1055 return Cow::Owned(out);
1056 }
1057}
1058
1059
1060#[cfg(test)]
1061mod tests
1062{
1063 use super::*;
1064
1065 #[test]
1066 fn test_truncate()
1067 {
1068 let test = "cat\n";
1069
1070 let trunc = truncate(test);
1071
1072 assert_eq!("cat", trunc);
1073 }
1074
1075
1076
1077 #[test]
1078 fn test_truncate_n()
1079 {
1080 assert_eq!(truncate_n("abcde", 3), "abc");
1081 assert_eq!(truncate_n("ボルテ", 4), "ボ");
1082 assert_eq!(truncate_n("ボルテ", 5), "ボ");
1083 assert_eq!(truncate_n("ボルテ", 6), "ボル");
1084 assert_eq!(truncate_n("abcde", 0), "");
1085 assert_eq!(truncate_n("abcde", 5), "abcde");
1086 assert_eq!(truncate_n("abcde", 6), "abcde");
1087 assert_eq!(truncate_n("ДАТА", 3), "Д");
1088 assert_eq!(truncate_n("ДАТА", 4), "ДА");
1089 assert_eq!(truncate_n("ДАТА", 1), "");
1090 }
1091
1092}