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 use crate::error::SyRes;
297
298 use super::throw_error;
299
300 bitflags! {
301 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
303 pub struct LogStat: libc::c_int
304 {
305 const LOG_PID = libc::LOG_PID;
307
308 const LOG_CONS = libc::LOG_CONS;
311
312 const LOG_ODELAY = libc::LOG_ODELAY;
316
317 const LOG_NDELAY = libc::LOG_NDELAY;
319
320 const LOG_NOWAIT = libc::LOG_NOWAIT;
323
324 const LOG_PERROR = 0x20;
326 }
327 }
328
329 #[cfg(feature = "build_sync")]
330 impl LogStat
331 {
332 #[inline]
333 pub(crate)
334 fn send_to_stderr(&self, msg: &str)
335 {
336 if self.intersects(LogStat::LOG_PERROR) == true
337 {
338 let stderr_lock = std::io::stderr().lock();
339 let newline = "\n";
340
341 let _ = send_to_fd(stderr_lock, msg, &newline);
342 }
343 }
344
345 #[inline]
346 pub(crate)
347 fn send_to_syscons(&self, msg_payload: &str)
348 {
349 use std::fs::File;
350 use std::os::unix::fs::OpenOptionsExt;
351
352 if self.intersects(LogStat::LOG_CONS)
353 {
354 use crate::PATH_CONSOLE;
355
356 let syscons =
357 File
358 ::options()
359 .create(false)
360 .read(false)
361 .write(true)
362 .custom_flags(libc::O_NONBLOCK | libc::O_CLOEXEC)
363 .open(*PATH_CONSOLE);
364
365 if let Ok(file) = syscons
366 {
367 let newline = "\n";
368 let _ = send_to_fd(file, msg_payload, newline);
369 }
370 }
371 }
372 }
373
374 #[allow(nonstandard_style)]
375 #[repr(i32)]
376 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
377 pub enum Priority
378 {
379 LOG_EMERG = libc::LOG_EMERG,
381
382 LOG_ALERT = libc::LOG_ALERT,
384
385 LOG_CRIT = libc::LOG_CRIT,
387
388 LOG_ERR = libc::LOG_ERR,
390
391 LOG_WARNING = libc::LOG_WARNING,
393
394 LOG_NOTICE = libc::LOG_NOTICE,
396
397 LOG_INFO = libc::LOG_INFO,
399
400 LOG_DEBUG = libc::LOG_DEBUG,
402 }
403
404 impl fmt::Display for Priority
405 {
406 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
407 {
408 match self
411 {
412 Self::LOG_EMERG =>
413 write!(f, "[EMERG]"),
414 Self::LOG_ALERT =>
415 write!(f, "[ALERT]"),
416 Self::LOG_CRIT =>
417 write!(f, "[CRIT]"),
418 Self::LOG_ERR =>
419 write!(f, "[ERR]"),
420 Self::LOG_WARNING =>
421 write!(f, "[WARNING]"),
422 Self::LOG_NOTICE =>
423 write!(f, "[NOTICE]"),
424 Self::LOG_INFO =>
425 write!(f, "[INFO]"),
426 Self::LOG_DEBUG =>
427 write!(f, "[DEBUG]"),
428 }
429 }
430 }
431
432
433 impl Priority
434 {
435
436 }
437
438 bitflags! {
439 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
442 pub struct LogFacility: libc::c_int
443 {
444 const LOG_KERN = libc::LOG_KERN;
446
447 const LOG_USER = libc::LOG_USER;
449
450 const LOG_MAIL = libc::LOG_MAIL;
452
453 const LOG_DAEMON = libc::LOG_DAEMON;
455
456 const LOG_AUTH = libc::LOG_AUTH;
458
459 const LOG_SYSLOG = libc::LOG_SYSLOG;
461
462 const LOG_LPR = libc::LOG_LPR;
464
465 const LOG_NEWS = libc::LOG_NEWS;
467
468 const LOG_UUCP = libc::LOG_UUCP;
470
471 const LOG_LOCAL0 = libc::LOG_LOCAL0;
473
474 const LOG_LOCAL1 = libc::LOG_LOCAL1;
476
477 const LOG_LOCAL2 = libc::LOG_LOCAL2;
479
480 const LOG_LOCAL3 = libc::LOG_LOCAL3;
482
483 const LOG_LOCAL4 = libc::LOG_LOCAL4;
485
486 const LOG_LOCAL5 = libc::LOG_LOCAL5;
488
489 const LOG_LOCAL6 = libc::LOG_LOCAL6;
491
492 const LOG_LOCAL7 = libc::LOG_LOCAL7;
494 }
495 }
496
497
498
499
500 pub const PATH_LOG: &'static str = "/var/run/log";
502
503 pub const PATH_LOG_PRIV: &'static str = "/var/run/logpriv";
505
506 pub const PATH_OLDLOG: &'static str = "/dev/log";
508
509 pub const PATH_OSX: &'static str = "/var/run/syslog";
511
512 #[cfg(feature = "build_sync")]
521 pub(crate) mod sync_portion
522 {
523 use std::io::Write;
524 use std::io::IoSlice;
525 use crate::error::SyRes;
526 use crate::map_error_os;
527
528 pub(crate)
539 fn send_to_fd<W>(mut file_fd: W, msg: &str, newline: &str) -> SyRes<usize>
540 where W: Write
541 {
542 return
543 file_fd
544 .write_vectored(
545 &[IoSlice::new(msg.as_bytes()), IoSlice::new(newline.as_bytes())]
546 )
547 .map_err(|e|
548 map_error_os!(e, "send_to_fd() writev() failed")
549 );
550 }
551 }
552
553 #[cfg(feature = "build_sync")]
554 pub(crate) use self::sync_portion::*;
555
556 #[cfg(test)]
557 mod tests
558 {
559 use super::*;
560
561 #[cfg(feature = "build_sync")]
562 #[test]
563 fn test_error_message()
564 {
565 let testmsg = "this is test message!";
571 let newline = "\n";
572 let stderr_lock = std::io::stderr().lock();
573 let res = send_to_fd(stderr_lock, testmsg, &newline);
574
575 println!("res: {:?}", res);
576
577 assert_eq!(res.is_ok(), true, "err: {}", res.err().unwrap());
578
579 return;
580 }
581
582 #[test]
583 fn test_priority_shl()
584 {
585 assert_eq!((1 << 5), (1 << Priority::LOG_NOTICE));
586 }
587 }
588}
589
590pub static PATH_CONSOLE: LazyLock<&Path> = LazyLock::new(||
592 {
593 Path::new("/dev/console")
594 }
595);
596
597pub static RFC5424_MAX_DGRAM: LazyLock<usize> = LazyLock::new(||
599 {
600 portable::get_local_dgram_maxdgram() as usize
601 }
602);
603
604
605pub const MAXHOSTNAMELEN: usize = 256;
607
608pub const LOG_FACMASK: i32 = 0x03f8;
610
611pub const MAXLINE: usize = 8192;
615
616pub const RFC3164_MAX_PAYLOAD_LEN: usize = 1024;
618
619pub const WINDOWS_EVENT_REPORT_MAX_PAYLOAD_LEN: usize = 31839;
621
622#[cfg(all(feature = "udp_truncate_1024_bytes", feature = "udp_truncate_1440_bytes"))]
623compile_error!("either 'udp_truncate_1024_bytes' or 'udp_truncate_1440_bytes' should be enabled");
624
625#[cfg(feature = "udp_truncate_1024_bytes")]
627pub const RFC5424_UDP_MAX_PKT_LEN: usize = 1024;
628
629#[cfg(any(feature = "udp_truncate_1440_bytes", all(not(feature = "udp_truncate_1440_bytes"), not(feature = "udp_truncate_1024_bytes"))))]
630pub const RFC5424_UDP_MAX_PKT_LEN: usize = 2048;
631
632#[cfg(feature = "tcp_truncate_1024_bytes")]
633pub const RFC5424_TCP_MAX_PKT_LEN: usize = 1024;
634
635#[cfg(feature = "tcp_truncate_2048_bytes")]
636pub const RFC5424_TCP_MAX_PKT_LEN: usize = 2048;
637
638#[cfg(feature = "tcp_truncate_4096_bytes")]
639pub const RFC5424_TCP_MAX_PKT_LEN: usize = 4096;
640
641#[cfg(feature = "tcp_truncate_max_bytes")]
642pub const RFC5424_TCP_MAX_PKT_LEN: usize = MAXLINE;
643
644pub const RFC_MAX_APP_NAME: usize = 48;
646
647pub const IANA_PRIV_ENT_NUM: u64 = 32473;
649
650pub const NILVALUE: &'static str = "-";
652
653pub const ESC_CHAR_REPL: &'static str = "#000";
655
656pub const NILVALUE_B: &'static [u8] = b"-";
658
659pub const WSPACE: &'static str = " ";
661
662pub const OBRACE: &'static str = "[";
664
665pub const CBRACE: &'static str = "]";
667
668pub const CBRACE_SEM: &'static str = "]:";
670
671pub const QCHAR: &'static str = "\"";
673
674pub const ATSIGN: &'static str = "@";
676
677pub const EQSIGN: &'static str = "=";
679
680pub const NEXTLINE: &'static str = "\n";
682
683bitflags! {
684 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
686 pub(crate) struct LogMask: i32
687 {
688 const LOG_FACMASK = 0x3f8;
690
691 const LOG_PRIMASK = 7;
693 }
694}
695
696#[derive(Debug, Clone, Copy, PartialEq, Eq)]
702pub struct SyslogMsgPriFac(i32);
703
704impl fmt::Display for SyslogMsgPriFac
705{
706 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
707 {
708 write!(f, "{}", self.0)
709 }
710}
711
712impl TryFrom<i32> for SyslogMsgPriFac
713{
714 type Error = SyslogError;
715
716 fn try_from(value: i32) -> Result<Self, Self::Error>
717 {
718 if (value & !(LogMask::LOG_PRIMASK | LogMask::LOG_FACMASK )) != 0
719 {
720 throw_error!("unknwon facility/priority: {:x}", value);
721 }
722
723 return Ok(Self(value));
724 }
725}
726
727impl TryFrom<&[u8]> for SyslogMsgPriFac
728{
729 type Error = SyslogError;
730
731 fn try_from(value: &[u8]) -> Result<Self, Self::Error>
732 {
733 let val =
734 str::from_utf8(value)
735 .map_or(
736 None,
737 |pri_str|
738 {
739 i32::from_str_radix(pri_str, 10)
740 .map_or(
741 None,
742 |val|
743 Some(val & LogMask::LOG_PRIMASK | val & LogMask::LOG_FACMASK.bits())
744 )
745 }
746 )
747 .ok_or(
748 map_error_code!(InternalError, "cannot convert to prio|facility")
749 )?;
750
751 if (val & !(LogMask::LOG_PRIMASK | LogMask::LOG_FACMASK )) != 0
752 {
753 throw_error!("unknwon facility/priority: {:x}", val);
754 }
755
756 return Ok(Self(val));
757 }
758}
759
760impl SyslogMsgPriFac
761{
762 pub(crate)
765 fn set_facility(p: Priority, f: LogFacility) -> Self
766 {
767 return Self( p as i32 | f.bits() );
768 }
769
770 pub
772 fn get_val(&self) -> i32
773 {
774 return self.0;
775 }
776
777 pub
779 fn get_priority(&self) -> Priority
780 {
781 return Priority::from(self.0 & LogMask::LOG_PRIMASK);
782 }
783
784 pub
786 fn get_log_facility(&self) -> LogFacility
787 {
788 return LogFacility::from_bits_retain(self.0 & LogMask::LOG_FACMASK);
789 }
790}
791
792impl From<i32> for Priority
793{
794 fn from(value: i32) -> Self
795 {
796 if value == Priority::LOG_ALERT as i32
797 {
798 return Priority::LOG_ALERT;
799 }
800 else if value == Priority::LOG_CRIT as i32
801 {
802 return Priority::LOG_CRIT;
803 }
804 else if value == Priority::LOG_DEBUG as i32
805 {
806 return Priority::LOG_DEBUG;
807 }
808 else if value == Priority::LOG_EMERG as i32
809 {
810 return Priority::LOG_EMERG;
811 }
812 else if value == Priority::LOG_ERR as i32
813 {
814 return Priority::LOG_ERR;
815 }
816 else if value == Priority::LOG_INFO as i32
817 {
818 return Priority::LOG_INFO;
819 }
820 else if value == Priority::LOG_NOTICE as i32
821 {
822 return Priority::LOG_NOTICE;
823 }
824 else
825 {
826 return Priority::LOG_WARNING;
827 }
828 }
829}
830
831#[macro_export]
842macro_rules! LOG_MASK
843{
844 ($arg:expr) => (
845 (1 << ($arg))
846 )
847}
848
849#[macro_export]
859macro_rules! LOG_UPTO
860{
861 ($arg:expr) => (
862 ((1 << (($arg) + 1)) - 1)
863 )
864}
865
866impl Shl<Priority> for i32
867{
868 type Output = i32;
869
870 fn shl(self, rhs: Priority) -> i32
871 {
872 let lhs = self;
873 return lhs << rhs as i32;
874 }
875}
876
877impl BitAnd<Priority> for i32
878{
879 type Output = i32;
880
881 #[inline]
882 fn bitand(self, rhs: Priority) -> i32
883 {
884 return self & rhs as i32;
885 }
886}
887
888impl BitAnd<LogMask> for Priority
889{
890 type Output = i32;
891
892 #[inline]
893 fn bitand(self, rhs: LogMask) -> i32
894 {
895 return self as i32 & rhs.bits();
896 }
897}
898
899impl BitAnd<LogMask> for LogFacility
900{
901 type Output = LogFacility;
902
903 #[inline]
904 fn bitand(self, rhs: LogMask) -> Self::Output
905 {
906 return Self::from_bits_retain(self.bits() & rhs.bits());
907 }
908}
909
910impl BitAnd<LogMask> for i32
911{
912 type Output = i32;
913
914 #[inline]
915 fn bitand(self, rhs: LogMask) -> i32
916 {
917 return self & rhs.bits();
918 }
919}
920
921pub
931fn truncate(lt: &str) -> &str
932{
933 let ltt =
934 match lt.char_indices().nth(lt.len()-1)
935 {
936 None => lt,
937 Some((idx, _)) => <[..idx],
938 };
939 return ltt;
940}
941
942pub
966fn truncate_n<'t>(lt: &'t str, n: usize) -> &'t str
967{
968 if lt.as_bytes().len() <= n
969 {
970 return lt;
971 }
972
973 let mut nn: usize = 0;
974 let mut cc = lt.chars();
975 let mut ln: usize;
976
977 loop
978 {
979 match cc.next()
980 {
981 Some(r) =>
982 {
983 ln = r.len_utf8();
984 nn += ln;
985
986 if nn == n
987 {
988 return <[..nn];
989 }
990 else if nn > n
991 {
992 return <[..nn-ln];
993 }
994 },
995 None =>
996 return lt,
997 }
998 }
999}
1000
1001pub
1010fn check_printable(a: &str) -> SyRes<()>
1011{
1012 if a.is_empty() == true
1013 {
1014 throw_error!("empty SD value");
1015 }
1016
1017 for p in a.chars()
1018 {
1019 if p.is_ascii() == false || p.is_ascii_graphic() == false || p == '@' || p == '=' || p == ']' || p == '\"'
1020 {
1021 throw_error!("incorrect char: '{:X}' in SD value", p as u64);
1022 }
1023 }
1024
1025 return Ok(());
1026}
1027
1028
1029pub
1030fn escape_chars(st: Cow<'static, str>) -> Cow<'static, str>
1031{
1032 let mut out = String::with_capacity(st.len());
1033
1034 for c in st.chars()
1035 {
1036 if c.is_control() == true
1037 {
1038 out.push_str(ESC_CHAR_REPL);
1039 }
1040 else if c == '\"' || c == '\\' || c == ']'
1041 {
1042 out.push('\\');
1043 out.push(c);
1044 }
1045 else
1046 {
1047 out.push(c);
1048 }
1049 }
1050
1051 if st.len() == out.len()
1052 {
1053 return st;
1054 }
1055 else
1056 {
1057 return Cow::Owned(out);
1058 }
1059}
1060
1061
1062#[cfg(test)]
1063mod tests
1064{
1065 use super::*;
1066
1067 #[test]
1068 fn test_truncate()
1069 {
1070 let test = "cat\n";
1071
1072 let trunc = truncate(test);
1073
1074 assert_eq!("cat", trunc);
1075 }
1076
1077
1078
1079 #[test]
1080 fn test_truncate_n()
1081 {
1082 assert_eq!(truncate_n("abcde", 3), "abc");
1083 assert_eq!(truncate_n("ボルテ", 4), "ボ");
1084 assert_eq!(truncate_n("ボルテ", 5), "ボ");
1085 assert_eq!(truncate_n("ボルテ", 6), "ボル");
1086 assert_eq!(truncate_n("abcde", 0), "");
1087 assert_eq!(truncate_n("abcde", 5), "abcde");
1088 assert_eq!(truncate_n("abcde", 6), "abcde");
1089 assert_eq!(truncate_n("ДАТА", 3), "Д");
1090 assert_eq!(truncate_n("ДАТА", 4), "ДА");
1091 assert_eq!(truncate_n("ДАТА", 1), "");
1092 }
1093
1094}