Skip to main content

syslog_rs/
common.rs

1/*-
2 * syslog-rs - a syslog client translated from libc to rust
3 * 
4 * Copyright 2025 Aleksandr Morozov
5 * 
6 * The syslog-rs crate can be redistributed and/or modified
7 * under the terms of either of the following licenses:
8 *
9 *   1. the Mozilla Public License Version 2.0 (the “MPL”) OR
10 *
11 *   2. The MIT License (MIT)
12 *                     
13 *   3. EUROPEAN UNION PUBLIC LICENCE v. 1.2 EUPL © the European Union 2007, 2016
14 */
15
16
17
18
19use std::{borrow::Cow, fmt, ops::{Add, 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         /// system is unusable
43        LOG_EMERG = 0,
44
45        /// action must be taken immediately
46        LOG_ALERT = 1,
47
48        /// critical conditions
49        LOG_CRIT =  2,
50
51        /// error conditions
52        LOG_ERR =  3,
53
54        /// warning conditions
55        LOG_WARNING = 4,
56
57        /// normal, but significant, condition
58        LOG_NOTICE = 5,
59        
60        /// informational message
61        LOG_INFO = 6,
62
63        /// debug-level message
64        LOG_DEBUG = 7,
65    }
66  
67    impl fmt::Display for Priority
68    {
69        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result 
70        {
71            //let pri = self.bits & LogMask::LOG_PRIMASK;
72
73            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        /*
116        /// This function validates the `pri` for the incorrects bits set.
117        /// If bits are set incorrectly, resets the invalid bits with:
118        /// *pri & (LogMask::LOG_PRIMASK | LogMask::LOG_FACMASK).
119        ///
120        /// # Arguments
121        ///
122        /// * `pri` - a priority bits
123        ///
124        /// # Returns
125        /// 
126        /// * A [SyRes]. Ok() when valid or Err with error message
127        pub(crate) 
128        fn check_invalid_bits(&mut self) -> SyRes<()>
129        {
130        
131            if (self.bits() & !(LogMask::LOG_PRIMASK | LogMask::LOG_FACMASK )) != 0
132            {
133                let pri_old = self.clone();
134                
135                *self = Self::from_bits_retain(self.bits() & (LogMask::LOG_PRIMASK | LogMask::LOG_FACMASK).bits() );
136
137                throw_error!("unknwon facility/priority: {:x}", pri_old);
138            }
139
140            return Ok(());
141        }*/
142    }
143
144    
145
146    bitflags! {
147        /// Controls  the  operation  of openlog() and subsequent calls to syslog.
148        #[derive(Clone, Copy, Debug, PartialEq, Eq)]
149        pub struct LogStat: i32 
150        {
151            /// Log the process ID with each message. (!todo)
152            const LOG_PID = 1;
153            
154            /// Write directly to the system console if there is an error 
155            /// while sending to the system logger.
156            const LOG_CONS = 2;
157
158            /// The converse of LOG_NDELAY; opening of the connection is delayed 
159            /// until syslog() is called. (This is the default behaviour,and need 
160            /// not be specified.)
161            const LOG_ODELAY = 0;
162
163            /// Open the connection immediately
164            const LOG_NDELAY = 0;
165
166            /// Don't wait for child processes that may have been created 
167            /// while logging the message
168            const LOG_NOWAIT = 0;
169            
170            /// Also log the message to stderr
171            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        /// The facility argument is used to specify what type of program 
201        /// is logging the message.
202        #[derive(Clone, Copy, Debug, PartialEq, Eq)]
203        pub struct LogFacility: i32 
204        {
205            /// kernel messages (these can't be generated from user processes)
206            const LOG_KERN = 0;
207
208            /// (default) generic user-level messages
209            const LOG_USER = 8;
210
211            /// mail subsystem
212            const LOG_MAIL = 16;
213
214            /// system daemons without separate facility value
215            const LOG_DAEMON = 24;
216            
217            /// security/authorization messages
218            const LOG_AUTH = 32;
219
220            /// messages generated internally by syslogd(8)
221            const LOG_SYSLOG = 40;
222
223            /// line printer subsystem
224            const LOG_LPR = 48;
225
226            /// USENET news subsystem
227            const LOG_NEWS = 56;
228
229            /// UUCP subsystem
230            const LOG_UUCP = 64;
231
232            /// reserved for local use
233            const LOG_LOCAL0 = 128;
234
235            /// reserved for local use
236            const LOG_LOCAL1 = 136;
237
238            /// reserved for local use
239            const LOG_LOCAL2 = 144;
240            
241            /// reserved for local use
242            const LOG_LOCAL3 = 152;
243
244            /// reserved for local use
245            const LOG_LOCAL4 = 160;
246
247            /// reserved for local use
248            const LOG_LOCAL5 = 168;
249
250            /// reserved for local use
251            const LOG_LOCAL6 = 176;
252            
253            /// reserved for local use
254            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, str::FromStr};
295
296
297    bitflags! {
298        /// Controls  the  operation  of openlog() and subsequent calls to syslog.
299        #[derive(Clone, Copy, Debug, PartialEq, Eq)]
300        pub struct LogStat: libc::c_int 
301        {
302            /// Log the process ID with each message. (!todo)
303            const LOG_PID = libc::LOG_PID;
304            
305            /// Write directly to the system console if there is an error 
306            /// while sending to the system logger.
307            const LOG_CONS = libc::LOG_CONS;
308
309            /// The converse of LOG_NDELAY; opening of the connection is delayed 
310            /// until syslog() is called. (This is the default behaviour,and need 
311            /// not be specified.)
312            const LOG_ODELAY = libc::LOG_ODELAY;
313
314            /// Open the connection immediately
315            const LOG_NDELAY = libc::LOG_NDELAY;
316
317            /// Don't wait for child processes that may have been created 
318            /// while logging the message
319            const LOG_NOWAIT = libc::LOG_NOWAIT;
320            
321            /// Also log the message to stderr
322            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         /// system is unusable
377        LOG_EMERG = libc::LOG_EMERG,
378
379        /// action must be taken immediately
380        LOG_ALERT = libc::LOG_ALERT,
381
382        /// critical conditions
383        LOG_CRIT =  libc::LOG_CRIT,
384
385        /// error conditions
386        LOG_ERR =  libc::LOG_ERR,
387
388        /// warning conditions
389        LOG_WARNING = libc::LOG_WARNING,
390
391        /// normal, but significant, condition
392        LOG_NOTICE = libc::LOG_NOTICE,
393        
394        /// informational message
395        LOG_INFO = libc::LOG_INFO,
396
397        /// debug-level message
398        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            //let pri = self.bits & LogMask::LOG_PRIMASK;
406
407            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    impl FromStr for Priority
430    {
431        type Err = SyslogError;
432    
433        fn from_str(s: &str) -> Result<Self, Self::Err> 
434        {
435            let ss = s.trim().to_uppercase();
436
437            match ss.as_str()
438            {
439                "LOG_EMERG" => Ok(Self::LOG_EMERG),
440                "LOG_ALERT" => Ok(Self::LOG_ALERT),
441                "LOG_CRIT" => Ok(Self::LOG_CRIT),
442                "LOG_ERR" => Ok(Self::LOG_ERR),
443                "LOG_WARNING" => Ok(Self::LOG_WARNING),
444                "LOG_NOTICE" => Ok(Self::LOG_NOTICE),
445                "LOG_INFO" => Ok(Self::LOG_INFO),
446                "LOG_DEBUG" => Ok(Self::LOG_DEBUG),
447                n => 
448                    throw_error!("unknown priority: {}", n)
449            }
450
451        }
452    }
453
454    impl Priority
455    {
456    
457    }
458
459    bitflags! {
460        /// The facility argument is used to specify what type of program 
461        /// is logging the message.
462        #[derive(Clone, Copy, Debug, PartialEq, Eq)]
463        pub struct LogFacility: libc::c_int 
464        {
465            /// kernel messages (these can't be generated from user processes)
466            const LOG_KERN = libc::LOG_KERN;
467
468            /// (default) generic user-level messages
469            const LOG_USER = libc::LOG_USER;
470
471            /// mail subsystem
472            const LOG_MAIL = libc::LOG_MAIL;
473
474            /// system daemons without separate facility value
475            const LOG_DAEMON = libc::LOG_DAEMON;
476            
477            /// security/authorization messages
478            const LOG_AUTH = libc::LOG_AUTH;
479
480            /// messages generated internally by syslogd(8)
481            const LOG_SYSLOG = libc::LOG_SYSLOG;
482
483            /// line printer subsystem
484            const LOG_LPR = libc::LOG_LPR;
485
486            /// USENET news subsystem
487            const LOG_NEWS = libc::LOG_NEWS;
488
489            /// UUCP subsystem
490            const LOG_UUCP = libc::LOG_UUCP;
491
492            /// reserved for local use
493            const LOG_LOCAL0 = libc::LOG_LOCAL0;
494
495            /// reserved for local use
496            const LOG_LOCAL1 = libc::LOG_LOCAL1;
497
498            /// reserved for local use
499            const LOG_LOCAL2 = libc::LOG_LOCAL2;
500            
501            /// reserved for local use
502            const LOG_LOCAL3 = libc::LOG_LOCAL3;
503
504            /// reserved for local use
505            const LOG_LOCAL4 = libc::LOG_LOCAL4;
506
507            /// reserved for local use
508            const LOG_LOCAL5 = libc::LOG_LOCAL5;
509
510            /// reserved for local use
511            const LOG_LOCAL6 = libc::LOG_LOCAL6;
512            
513            /// reserved for local use
514            const LOG_LOCAL7 = libc::LOG_LOCAL7;
515        }
516    }
517
518
519    impl FromStr for LogFacility
520    {
521        type Err = SyslogError;
522    
523        fn from_str(s: &str) -> Result<Self, Self::Err> 
524        {
525            let s_up = s.to_ascii_uppercase();
526
527            match s_up.as_str()
528            {
529                "LOG_KERN" => Ok(Self::LOG_KERN),
530                "LOG_USER" => Ok(Self::LOG_USER),
531                "LOG_MAIL" => Ok(Self::LOG_MAIL),
532                "LOG_DAEMON" => Ok(Self::LOG_DAEMON),
533                "LOG_AUTH" => Ok(Self::LOG_AUTH),
534                "LOG_SYSLOG" => Ok(Self::LOG_SYSLOG),
535                "LOG_LPR" => Ok(Self::LOG_LPR),
536                "LOG_NEWS" => Ok(Self::LOG_NEWS),
537                "LOG_UUCP" => Ok(Self::LOG_UUCP),
538                "LOG_LOCAL0" => Ok(Self::LOG_LOCAL0),
539                "LOG_LOCAL1" => Ok(Self::LOG_LOCAL1),
540                "LOG_LOCAL2" => Ok(Self::LOG_LOCAL2),
541                "LOG_LOCAL3" => Ok(Self::LOG_LOCAL3),
542                "LOG_LOCAL4" => Ok(Self::LOG_LOCAL4),
543                "LOG_LOCAL5" => Ok(Self::LOG_LOCAL5),
544                "LOG_LOCAL6" => Ok(Self::LOG_LOCAL6),
545                "LOG_LOCAL7" => Ok(Self::LOG_LOCAL7),
546                n => 
547                    throw_error!("unknwon log facility: {}", n)
548            }
549        }
550    }
551
552    impl fmt::Display for LogFacility
553    {
554        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result 
555        {
556            match *self
557            {
558                Self::LOG_KERN => 
559                    write!(f, "LOG_KERN"),
560                Self::LOG_USER => 
561                    write!(f, "LOG_USER"),
562                Self::LOG_MAIL => 
563                    write!(f, "LOG_MAIL"),
564                Self::LOG_DAEMON => 
565                    write!(f, "LOG_DAEMON"),
566                Self::LOG_AUTH => 
567                    write!(f, "LOG_AUTH"),
568                Self::LOG_SYSLOG => 
569                    write!(f, "LOG_SYSLOG"),
570                Self::LOG_LPR => 
571                    write!(f, "LOG_LPR"),
572                Self::LOG_NEWS => 
573                    write!(f, "LOG_NEWS"),
574                Self::LOG_UUCP => 
575                    write!(f, "LOG_UUCP"),
576                Self::LOG_LOCAL0 => 
577                    write!(f, "LOG_LOCAL0"),
578                Self::LOG_LOCAL1 => 
579                    write!(f, "LOG_LOCAL1"),
580                Self::LOG_LOCAL2 => 
581                    write!(f, "LOG_LOCAL2"),
582                Self::LOG_LOCAL3 => 
583                    write!(f, "LOG_LOCAL3"),
584                Self::LOG_LOCAL4 => 
585                    write!(f, "LOG_LOCAL4"),
586                Self::LOG_LOCAL5 => 
587                    write!(f, "LOG_LOCAL5"),
588                Self::LOG_LOCAL6 => 
589                    write!(f, "LOG_LOCAL6"),
590                Self::LOG_LOCAL7 => 
591                    write!(f, "LOG_LOCAL7"),
592                n =>
593                    write!(f, "LOG_[{}]", n),
594            }
595        }
596    }
597
598    /// Unpriv socket
599    pub const PATH_LOG: &'static str = "/var/run/log";
600
601    /// Priviledged socket
602    pub const PATH_LOG_PRIV: &'static str = "/var/run/logpriv";
603
604    /// backward compatibility
605    pub const PATH_OLDLOG: &'static str = "/dev/log";
606
607    /// OSX compat
608    pub const PATH_OSX: &'static str = "/var/run/syslog";
609
610    /*
611    pub static PATH_CONSOLE: LazyLock<CString> = LazyLock::new(|| 
612        {
613            CString::new("/dev/console").unwrap()
614        }
615    );
616    */
617
618    #[cfg(feature = "build_sync")]
619    pub(crate) mod sync_portion
620    {
621        use std::io::Write;
622        use std::io::IoSlice;
623        use crate::error::SyRes;
624        use crate::map_error_os;
625
626        /// Sends to the FD i.e file of stderr, stdout or any which 
627        /// implements [Write] `write_vectored`.
628        ///
629        /// # Arguments
630        /// 
631        /// * `file_fd` - mutable consume of the container FD.
632        ///
633        /// * `msg` - a reference on array of data
634        ///
635        /// * `newline` - a new line string ref i.e "\n" or "\r\n"
636        pub(crate) 
637        fn send_to_fd<W>(mut file_fd: W, msg: &str, newline: &str) -> SyRes<usize>
638        where W: Write
639        {
640            return 
641                file_fd
642                    .write_vectored(
643                        &[IoSlice::new(msg.as_bytes()), IoSlice::new(newline.as_bytes())]
644                    )
645                    .map_err(|e|
646                        map_error_os!(e, "send_to_fd() writev() failed")
647                    );
648        }
649    }
650
651    use crate::{error::SyslogError, throw_error};
652
653#[cfg(feature = "build_sync")]
654    pub(crate) use self::sync_portion::*;
655
656    #[cfg(test)]
657    mod tests
658    {
659        use super::*;
660
661        #[cfg(feature = "build_sync")]
662        #[test]
663        fn test_error_message()
664        {
665            /*use std::sync::Arc;
666            use std::thread;
667            use std::time::Duration;
668            use super::{LOG_MASK};*/
669
670            let testmsg = "this is test message!";
671            let newline = "\n";
672            let stderr_lock = std::io::stderr().lock();
673            let res = send_to_fd(stderr_lock, testmsg, &newline);
674
675            println!("res: {:?}", res);
676
677            assert_eq!(res.is_ok(), true, "err: {}", res.err().unwrap());
678        
679            return;
680        }
681
682        #[test]
683        fn test_priority_shl()
684        {
685            assert_eq!((1 << 5), (1 << Priority::LOG_NOTICE));
686        }
687    }
688}
689
690/// Path to console.
691pub static PATH_CONSOLE: LazyLock<&Path> = LazyLock::new(|| 
692    {
693        Path::new("/dev/console")
694    }
695);
696
697/// A max dgram init.
698pub static RFC5424_MAX_DGRAM: LazyLock<usize> = LazyLock::new(|| 
699    {
700        portable::get_local_dgram_maxdgram() as usize
701    }
702);
703
704
705/// max hostname size
706pub const MAXHOSTNAMELEN: usize = 256;
707
708/// mask to extract facility part
709pub const LOG_FACMASK: i32 = 0x03f8;
710
711/// Maximum number of characters of syslog message.
712/// According to RFC5424. However syslog-protocol also may state that 
713/// the max message will be defined by the transport layer.
714pub const MAXLINE: usize = 8192;
715
716/// RFC3164 limit
717pub const RFC3164_MAX_PAYLOAD_LEN: usize = 1024;
718
719/// A maximum message which could be passed to Windows Event Log
720pub const WINDOWS_EVENT_REPORT_MAX_PAYLOAD_LEN: usize = 31839;
721
722#[cfg(all(feature = "udp_truncate_1024_bytes", feature = "udp_truncate_1440_bytes"))]
723compile_error!("either 'udp_truncate_1024_bytes' or 'udp_truncate_1440_bytes' should be enabled");
724
725// RFC5424 480 octets or limited by the (transport) MAX_DGRAM_LEN or other.
726#[cfg(feature = "udp_truncate_1024_bytes")]
727pub const RFC5424_UDP_MAX_PKT_LEN: usize  = 1024;
728
729#[cfg(any(feature = "udp_truncate_1440_bytes", all(not(feature = "udp_truncate_1440_bytes"), not(feature = "udp_truncate_1024_bytes"))))]
730pub const RFC5424_UDP_MAX_PKT_LEN: usize  = 2048;
731
732#[cfg(feature = "tcp_truncate_1024_bytes")]
733pub const RFC5424_TCP_MAX_PKT_LEN: usize  = 1024;
734
735#[cfg(feature = "tcp_truncate_2048_bytes")]
736pub const RFC5424_TCP_MAX_PKT_LEN: usize  = 2048;
737
738#[cfg(feature = "tcp_truncate_4096_bytes")]
739pub const RFC5424_TCP_MAX_PKT_LEN: usize  = 4096;
740
741#[cfg(feature = "tcp_truncate_max_bytes")]
742pub const RFC5424_TCP_MAX_PKT_LEN: usize  = MAXLINE;
743
744/// A max byte lenth of APPNAME (NILVALUE / 1*48PRINTUSASCII)
745pub const RFC_MAX_APP_NAME: usize = 48;
746
747/// A private enterprise number.
748pub const IANA_PRIV_ENT_NUM: u64 = 32473;
749
750/// RFC5424 defined value.
751pub const NILVALUE: &'static str = "-";
752
753/// RFC5424 escape character.
754pub const ESC_CHAR_REPL: &'static str = "#000";
755
756/// RFC5424 defined value (bytes).
757pub const NILVALUE_B: &'static [u8] = b"-";
758
759/// White space
760pub const WSPACE: &'static str = " ";
761
762/// Opening brace ('[', ABNF )
763pub const OBRACE: &'static str = "[";
764
765/// Closing brace (']', ABNF %d93)
766pub const CBRACE: &'static str = "]";
767
768/// Closing brace RFC3...
769pub const CBRACE_SEM: &'static str = "]:";
770
771/// Quote-character ('"', ABNF %d34)
772pub const QCHAR: &'static str = "\"";
773
774/// At-sign ("@", ABNF %d64)
775pub const ATSIGN: &'static str = "@";
776
777/// Eq-sign ("=", ABNF %d61)
778pub const EQSIGN: &'static str = "=";
779
780/// A cursor return.
781pub const NEXTLINE: &'static str = "\n";
782
783bitflags! {
784    /// Masks
785    #[derive(Clone, Copy, Debug, PartialEq, Eq)]
786    pub(crate) struct LogMask: i32 
787    {
788        /// A mask for the [LogFacility]
789        const LOG_FACMASK = 0x3f8;
790
791        /// A mask for the [Priority]
792        const LOG_PRIMASK = 7;
793    }
794}
795
796
797/// A struct which contains syslog encoded Facility and Priority 
798/// is the following order:
799/// 
800/// - log_facility mask 0x3f8
801/// - priority mask 0x7
802#[derive(Debug, Clone, Copy, PartialEq, Eq)]
803pub struct SyslogMsgPriFac(i32);
804
805impl fmt::Display for SyslogMsgPriFac
806{
807    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result 
808    {
809        write!(f, "{}", self.0)
810    }
811}
812
813impl TryFrom<i32> for SyslogMsgPriFac
814{
815    type Error = SyslogError;
816
817    fn try_from(value: i32) -> Result<Self, Self::Error> 
818    {
819        if (value & !(LogMask::LOG_PRIMASK | LogMask::LOG_FACMASK )) != 0
820        {
821            throw_error!("unknwon facility/priority: {:x}", value);
822        }
823
824        return Ok(Self(value));
825    }
826}
827
828impl TryFrom<&[u8]> for SyslogMsgPriFac
829{
830    type Error = SyslogError;
831
832    fn try_from(value: &[u8]) -> Result<Self, Self::Error> 
833    {
834        let val = 
835            str::from_utf8(value)
836                .map_or(
837                    None, 
838                    |pri_str|
839                    {
840                        i32::from_str_radix(pri_str, 10)
841                            .map_or(
842                                None,
843                                |val|
844                                Some(val & LogMask::LOG_PRIMASK | val & LogMask::LOG_FACMASK.bits()) 
845                            )
846                    }
847                )
848                .ok_or(
849                    map_error_code!(InternalError, "cannot convert to prio|facility")
850                )?;
851
852        if (val & !(LogMask::LOG_PRIMASK | LogMask::LOG_FACMASK )) != 0
853        {
854            throw_error!("unknwon facility/priority: {:x}", val);
855        }
856
857        return Ok(Self(val));
858    }
859}
860
861impl SyslogMsgPriFac
862{
863    /// Initializes the instance by adding a priority and facility together
864    /// to form a message header.
865    pub(crate) 
866    fn set_facility(p: Priority, f: LogFacility) -> Self
867    {
868        return Self( p as i32 | f.bits() );
869    }
870
871    /// Returns the raw value.
872    pub 
873    fn get_val(&self) -> i32
874    {
875        return self.0;
876    }
877
878    /// Reads from the inner a [Priority].
879    pub 
880    fn get_priority(&self) -> Priority
881    {
882        return Priority::from(self.0 & LogMask::LOG_PRIMASK);
883    }
884
885    /// Reads from inner a [LogFacility].
886    pub 
887    fn get_log_facility(&self) -> LogFacility
888    {
889        return LogFacility::from_bits_retain(self.0 & LogMask::LOG_FACMASK);
890    }
891}
892
893impl From<i32> for Priority
894{
895    fn from(value: i32) -> Self 
896    {
897        if value == Priority::LOG_ALERT as i32
898        {
899            return Priority::LOG_ALERT;
900        }
901        else if value == Priority::LOG_CRIT as i32
902        {
903            return Priority::LOG_CRIT;
904        }
905        else if value == Priority::LOG_DEBUG as i32
906        {
907            return Priority::LOG_DEBUG;
908        }
909        else if value == Priority::LOG_EMERG as i32
910        {
911            return Priority::LOG_EMERG;
912        }
913        else if value == Priority::LOG_ERR as i32
914        {
915            return Priority::LOG_ERR;
916        }
917        else if value == Priority::LOG_INFO as i32
918        {
919            return Priority::LOG_INFO;
920        }
921        else if value == Priority::LOG_NOTICE as i32
922        {
923            return Priority::LOG_NOTICE;
924        }
925        else
926        {
927            return Priority::LOG_WARNING;
928        }
929    }
930}
931
932/// LOG_MASK is used to create the priority mask in setlogmask. 
933/// For a single Priority mask
934/// used with [Priority]
935/// can be used with | & ! bit operations LOG_MASK()
936///
937/// # Examples
938/// 
939/// ```ignore
940///     LOG_MASK!(Priority::LOG_ALERT) | LOG_MASK!(Priority::LOG_INFO)
941/// ```
942#[macro_export]
943macro_rules! LOG_MASK 
944{
945    ($arg:expr) => (
946        (1 << ($arg))
947    )
948}
949
950/// LOG_MASK is used to create the priority mask in setlogmask
951/// For a mask UPTO specified
952/// used with [Priority]
953///
954/// # Examples
955/// 
956/// ```ignore
957///     LOG_UPTO!(Priority::LOG_ALERT)
958/// ```
959#[macro_export]
960macro_rules! LOG_UPTO 
961{
962    ($arg:expr) => (
963        ((1 << (($arg) + 1)) - 1)
964    )
965}
966
967impl Shl<Priority> for i32
968{
969    type Output = i32;
970
971    fn shl(self, rhs: Priority) -> i32 
972    {
973        let lhs = self;
974        return lhs << rhs as i32;
975    }
976}
977
978impl BitAnd<Priority> for i32
979{
980    type Output = i32;
981
982    #[inline]
983    fn bitand(self, rhs: Priority) -> i32
984    {
985        return self & rhs as i32;
986    }
987}
988
989impl Add<Priority> for i32
990{
991    type Output = i32;
992    
993    fn add(self, rhs: Priority) -> Self::Output 
994    {
995        rhs as i32 + self
996    }
997}
998
999impl Add<i32> for Priority
1000{
1001    type Output = i32;
1002
1003    fn add(self, rhs: i32) -> Self::Output 
1004    {
1005        self as i32 + rhs
1006    }
1007}
1008
1009impl BitAnd<LogMask> for Priority
1010{
1011    type Output = i32;
1012
1013    #[inline]
1014    fn bitand(self, rhs: LogMask) -> i32
1015    {
1016        return self as i32 & rhs.bits();
1017    }
1018}
1019
1020impl BitAnd<LogMask> for LogFacility 
1021{
1022    type Output = LogFacility;
1023
1024    #[inline]
1025    fn bitand(self, rhs: LogMask) -> Self::Output
1026    {
1027        return Self::from_bits_retain(self.bits() & rhs.bits());
1028    }
1029}
1030
1031impl BitAnd<LogMask> for i32 
1032{
1033    type Output = i32;
1034
1035    #[inline]
1036    fn bitand(self, rhs: LogMask) -> i32
1037    {
1038        return self & rhs.bits();
1039    }
1040}
1041
1042/// This function trancated 1 last UTF8 character from the string.
1043///
1044/// # Arguments
1045///
1046/// * `lt` - a string which is trucated
1047/// 
1048/// # Returns
1049/// 
1050/// * A reference to the ctruncated string
1051pub 
1052fn truncate(lt: &str) -> &str
1053{
1054    let ltt =
1055        match lt.char_indices().nth(lt.len()-1) 
1056        {
1057            None => lt,
1058            Some((idx, _)) => &lt[..idx],
1059        };
1060    return ltt;
1061}
1062
1063/// Trancates the string up to closest to N byte equiv UTF8
1064///  if string exceeds size
1065/// 
1066/// For example:  
1067/// ボルテ 'e3 83 9c e3 83 ab e3 83 86' with N=3  
1068/// will give 'ボ'  
1069/// 
1070/// ボルテ 'e3 83 9c e3 83 ab e3 83 86' with N=4  
1071/// will give 'ボ' 
1072/// 
1073/// ボルテ 'e3 83 9c e3 83 ab e3 83 86' with N=1  
1074/// will give ''
1075/// 
1076/// # Arguments
1077///
1078/// * `lt` - a string to truncate
1079///
1080/// * `n` - a size (in bytes, not in chars)
1081/// 
1082/// # Returns 
1083///
1084/// * A reference to [str] with the time `'t` which corresponds to
1085/// the lifetile of the input argument `'t`.
1086pub 
1087fn truncate_n<'t>(lt: &'t str, n: usize) -> &'t str
1088{
1089    if lt.as_bytes().len() <= n
1090    {
1091        return lt;
1092    }
1093
1094    let mut nn: usize = 0;
1095    let mut cc = lt.chars();
1096    let mut ln: usize;
1097
1098    loop 
1099    {
1100        match cc.next()
1101        {
1102            Some(r) =>
1103            {
1104                ln = r.len_utf8();
1105                nn += ln;
1106
1107                if nn == n
1108                {
1109                    return &lt[..nn];
1110                }
1111                else if nn > n
1112                {
1113                    return &lt[..nn-ln];
1114                }
1115            },
1116            None => 
1117                return lt,
1118        }
1119    }
1120}
1121
1122/// Checks if string are:
1123/// ```text
1124/// NOT EMPTY
1125/// MUST be printable US-ASCII strings, and MUST
1126/// NOT contain an at-sign ('@', ABNF %d64), an equal-sign ('=', ABNF
1127/// %d61), a closing brace (']', ABNF %d93), a quote-character ('"',
1128/// ABNF %d34), whitespace, or control characters
1129/// ```
1130pub
1131fn check_printable(a: &str) -> SyRes<()>
1132{
1133    if a.is_empty() == true
1134    {
1135        throw_error!("empty SD value");
1136    }
1137
1138    for p in a.chars()
1139    {
1140        if p.is_ascii() == false || p.is_ascii_graphic() == false || p == '@' || p == '=' || p == ']' || p == '\"'
1141        {
1142            throw_error!("incorrect char: '{:X}' in SD value", p as u64);
1143        }
1144    }
1145
1146    return Ok(());
1147}
1148
1149
1150pub 
1151fn escape_chars(st: Cow<'static, str>) -> Cow<'static, str>
1152{
1153    let mut out = String::with_capacity(st.len());
1154
1155    for c in st.chars()
1156    {
1157        if c.is_control() == true
1158        {
1159            out.push_str(ESC_CHAR_REPL);
1160        }
1161        else if c == '\"' || c == '\\' || c == ']'
1162        {
1163            out.push('\\');
1164            out.push(c);
1165        }
1166        else
1167        {
1168            out.push(c);
1169        }
1170    }
1171
1172    if st.len() == out.len()
1173    {
1174        return st;
1175    }
1176    else
1177    {
1178        return Cow::Owned(out);
1179    }
1180}
1181
1182
1183#[cfg(test)]
1184mod tests
1185{
1186    use super::*;
1187
1188    #[test]
1189    fn test_truncate()
1190    {
1191        let test = "cat\n";
1192
1193        let trunc = truncate(test);
1194
1195        assert_eq!("cat", trunc);
1196    }
1197
1198
1199
1200    #[test]
1201    fn test_truncate_n()
1202    {
1203        assert_eq!(truncate_n("abcde", 3), "abc");
1204        assert_eq!(truncate_n("ボルテ", 4), "ボ");
1205        assert_eq!(truncate_n("ボルテ", 5), "ボ");
1206        assert_eq!(truncate_n("ボルテ", 6), "ボル");
1207        assert_eq!(truncate_n("abcde", 0), "");
1208        assert_eq!(truncate_n("abcde", 5), "abcde");
1209        assert_eq!(truncate_n("abcde", 6), "abcde");
1210        assert_eq!(truncate_n("ДАТА", 3), "Д");
1211        assert_eq!(truncate_n("ДАТА", 4), "ДА");
1212        assert_eq!(truncate_n("ДАТА", 1), "");
1213    }
1214
1215}