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