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/// A struct which contains syslog encoded Facility and Priority 
797/// is the following order:
798/// 
799/// - log_facility mask 0x3f8
800/// - priority mask 0x7
801#[derive(Debug, Clone, Copy, PartialEq, Eq)]
802pub struct SyslogMsgPriFac(i32);
803
804impl fmt::Display for SyslogMsgPriFac
805{
806    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result 
807    {
808        write!(f, "{}", self.0)
809    }
810}
811
812impl TryFrom<i32> for SyslogMsgPriFac
813{
814    type Error = SyslogError;
815
816    fn try_from(value: i32) -> Result<Self, Self::Error> 
817    {
818        if (value & !(LogMask::LOG_PRIMASK | LogMask::LOG_FACMASK )) != 0
819        {
820            throw_error!("unknwon facility/priority: {:x}", value);
821        }
822
823        return Ok(Self(value));
824    }
825}
826
827impl TryFrom<&[u8]> for SyslogMsgPriFac
828{
829    type Error = SyslogError;
830
831    fn try_from(value: &[u8]) -> Result<Self, Self::Error> 
832    {
833        let val = 
834            str::from_utf8(value)
835                .map_or(
836                    None, 
837                    |pri_str|
838                    {
839                        i32::from_str_radix(pri_str, 10)
840                            .map_or(
841                                None,
842                                |val|
843                                Some(val & LogMask::LOG_PRIMASK | val & LogMask::LOG_FACMASK.bits()) 
844                            )
845                    }
846                )
847                .ok_or(
848                    map_error_code!(InternalError, "cannot convert to prio|facility")
849                )?;
850
851        if (val & !(LogMask::LOG_PRIMASK | LogMask::LOG_FACMASK )) != 0
852        {
853            throw_error!("unknwon facility/priority: {:x}", val);
854        }
855
856        return Ok(Self(val));
857    }
858}
859
860impl SyslogMsgPriFac
861{
862    /// Initializes the instance by adding a priority and facility together
863    /// to form a message header.
864    pub(crate) 
865    fn set_facility(p: Priority, f: LogFacility) -> Self
866    {
867        return Self( p as i32 | f.bits() );
868    }
869
870    /// Returns the raw value.
871    pub 
872    fn get_val(&self) -> i32
873    {
874        return self.0;
875    }
876
877    /// Reads from the inner a [Priority].
878    pub 
879    fn get_priority(&self) -> Priority
880    {
881        return Priority::from(self.0 & LogMask::LOG_PRIMASK);
882    }
883
884    /// Reads from inner a [LogFacility].
885    pub 
886    fn get_log_facility(&self) -> LogFacility
887    {
888        return LogFacility::from_bits_retain(self.0 & LogMask::LOG_FACMASK);
889    }
890}
891
892impl From<i32> for Priority
893{
894    fn from(value: i32) -> Self 
895    {
896        if value == Priority::LOG_ALERT as i32
897        {
898            return Priority::LOG_ALERT;
899        }
900        else if value == Priority::LOG_CRIT as i32
901        {
902            return Priority::LOG_CRIT;
903        }
904        else if value == Priority::LOG_DEBUG as i32
905        {
906            return Priority::LOG_DEBUG;
907        }
908        else if value == Priority::LOG_EMERG as i32
909        {
910            return Priority::LOG_EMERG;
911        }
912        else if value == Priority::LOG_ERR as i32
913        {
914            return Priority::LOG_ERR;
915        }
916        else if value == Priority::LOG_INFO as i32
917        {
918            return Priority::LOG_INFO;
919        }
920        else if value == Priority::LOG_NOTICE as i32
921        {
922            return Priority::LOG_NOTICE;
923        }
924        else
925        {
926            return Priority::LOG_WARNING;
927        }
928    }
929}
930
931/// LOG_MASK is used to create the priority mask in setlogmask. 
932/// For a single Priority mask
933/// used with [Priority]
934/// can be used with | & ! bit operations LOG_MASK()
935///
936/// # Examples
937/// 
938/// ```ignore
939///     LOG_MASK!(Priority::LOG_ALERT) | LOG_MASK!(Priority::LOG_INFO)
940/// ```
941#[macro_export]
942macro_rules! LOG_MASK 
943{
944    ($arg:expr) => (
945        (1 << ($arg))
946    )
947}
948
949/// LOG_MASK is used to create the priority mask in setlogmask
950/// For a mask UPTO specified
951/// used with [Priority]
952///
953/// # Examples
954/// 
955/// ```ignore
956///     LOG_UPTO!(Priority::LOG_ALERT)
957/// ```
958#[macro_export]
959macro_rules! LOG_UPTO 
960{
961    ($arg:expr) => (
962        ((1 << (($arg) + 1)) - 1)
963    )
964}
965
966impl Shl<Priority> for i32
967{
968    type Output = i32;
969
970    fn shl(self, rhs: Priority) -> i32 
971    {
972        let lhs = self;
973        return lhs << rhs as i32;
974    }
975}
976
977impl BitAnd<Priority> for i32
978{
979    type Output = i32;
980
981    #[inline]
982    fn bitand(self, rhs: Priority) -> i32
983    {
984        return self & rhs as i32;
985    }
986}
987
988impl Add<Priority> for i32
989{
990    type Output = i32;
991    
992    fn add(self, rhs: Priority) -> Self::Output 
993    {
994        rhs as i32 + self
995    }
996}
997
998impl Add<i32> for Priority
999{
1000    type Output = i32;
1001
1002    fn add(self, rhs: i32) -> Self::Output 
1003    {
1004        self as i32 + rhs
1005    }
1006}
1007
1008impl BitAnd<LogMask> for Priority
1009{
1010    type Output = i32;
1011
1012    #[inline]
1013    fn bitand(self, rhs: LogMask) -> i32
1014    {
1015        return self as i32 & rhs.bits();
1016    }
1017}
1018
1019impl BitAnd<LogMask> for LogFacility 
1020{
1021    type Output = LogFacility;
1022
1023    #[inline]
1024    fn bitand(self, rhs: LogMask) -> Self::Output
1025    {
1026        return Self::from_bits_retain(self.bits() & rhs.bits());
1027    }
1028}
1029
1030impl BitAnd<LogMask> for i32 
1031{
1032    type Output = i32;
1033
1034    #[inline]
1035    fn bitand(self, rhs: LogMask) -> i32
1036    {
1037        return self & rhs.bits();
1038    }
1039}
1040
1041/// This function trancated 1 last UTF8 character from the string.
1042///
1043/// # Arguments
1044///
1045/// * `lt` - a string which is trucated
1046/// 
1047/// # Returns
1048/// 
1049/// * A reference to the ctruncated string
1050pub 
1051fn truncate(lt: &str) -> &str
1052{
1053    let ltt =
1054        match lt.char_indices().nth(lt.len()-1) 
1055        {
1056            None => lt,
1057            Some((idx, _)) => &lt[..idx],
1058        };
1059    return ltt;
1060}
1061
1062/// Trancates the string up to closest to N byte equiv UTF8
1063///  if string exceeds size
1064/// 
1065/// For example:  
1066/// ボルテ 'e3 83 9c e3 83 ab e3 83 86' with N=3  
1067/// will give 'ボ'  
1068/// 
1069/// ボルテ 'e3 83 9c e3 83 ab e3 83 86' with N=4  
1070/// will give 'ボ' 
1071/// 
1072/// ボルテ 'e3 83 9c e3 83 ab e3 83 86' with N=1  
1073/// will give ''
1074/// 
1075/// # Arguments
1076///
1077/// * `lt` - a string to truncate
1078///
1079/// * `n` - a size (in bytes, not in chars)
1080/// 
1081/// # Returns 
1082///
1083/// * A reference to [str] with the time `'t` which corresponds to
1084/// the lifetile of the input argument `'t`.
1085pub 
1086fn truncate_n<'t>(lt: &'t str, n: usize) -> &'t str
1087{
1088    if lt.as_bytes().len() <= n
1089    {
1090        return lt;
1091    }
1092
1093    let mut nn: usize = 0;
1094    let mut cc = lt.chars();
1095    let mut ln: usize;
1096
1097    loop 
1098    {
1099        match cc.next()
1100        {
1101            Some(r) =>
1102            {
1103                ln = r.len_utf8();
1104                nn += ln;
1105
1106                if nn == n
1107                {
1108                    return &lt[..nn];
1109                }
1110                else if nn > n
1111                {
1112                    return &lt[..nn-ln];
1113                }
1114            },
1115            None => 
1116                return lt,
1117        }
1118    }
1119}
1120
1121/// Checks if string are:
1122/// ```text
1123/// NOT EMPTY
1124/// MUST be printable US-ASCII strings, and MUST
1125/// NOT contain an at-sign ('@', ABNF %d64), an equal-sign ('=', ABNF
1126/// %d61), a closing brace (']', ABNF %d93), a quote-character ('"',
1127/// ABNF %d34), whitespace, or control characters
1128/// ```
1129pub
1130fn check_printable(a: &str) -> SyRes<()>
1131{
1132    if a.is_empty() == true
1133    {
1134        throw_error!("empty SD value");
1135    }
1136
1137    for p in a.chars()
1138    {
1139        if p.is_ascii() == false || p.is_ascii_graphic() == false || p == '@' || p == '=' || p == ']' || p == '\"'
1140        {
1141            throw_error!("incorrect char: '{:X}' in SD value", p as u64);
1142        }
1143    }
1144
1145    return Ok(());
1146}
1147
1148
1149pub 
1150fn escape_chars(st: Cow<'static, str>) -> Cow<'static, str>
1151{
1152    let mut out = String::with_capacity(st.len());
1153
1154    for c in st.chars()
1155    {
1156        if c.is_control() == true
1157        {
1158            out.push_str(ESC_CHAR_REPL);
1159        }
1160        else if c == '\"' || c == '\\' || c == ']'
1161        {
1162            out.push('\\');
1163            out.push(c);
1164        }
1165        else
1166        {
1167            out.push(c);
1168        }
1169    }
1170
1171    if st.len() == out.len()
1172    {
1173        return st;
1174    }
1175    else
1176    {
1177        return Cow::Owned(out);
1178    }
1179}
1180
1181
1182#[cfg(test)]
1183mod tests
1184{
1185    use super::*;
1186
1187    #[test]
1188    fn test_truncate()
1189    {
1190        let test = "cat\n";
1191
1192        let trunc = truncate(test);
1193
1194        assert_eq!("cat", trunc);
1195    }
1196
1197
1198
1199    #[test]
1200    fn test_truncate_n()
1201    {
1202        assert_eq!(truncate_n("abcde", 3), "abc");
1203        assert_eq!(truncate_n("ボルテ", 4), "ボ");
1204        assert_eq!(truncate_n("ボルテ", 5), "ボ");
1205        assert_eq!(truncate_n("ボルテ", 6), "ボル");
1206        assert_eq!(truncate_n("abcde", 0), "");
1207        assert_eq!(truncate_n("abcde", 5), "abcde");
1208        assert_eq!(truncate_n("abcde", 6), "abcde");
1209        assert_eq!(truncate_n("ДАТА", 3), "Д");
1210        assert_eq!(truncate_n("ДАТА", 4), "ДА");
1211        assert_eq!(truncate_n("ДАТА", 1), "");
1212    }
1213
1214}