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::{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
430    impl Priority
431    {
432    
433    }
434
435    bitflags! {
436        /// The facility argument is used to specify what type of program 
437        /// is logging the message.
438        #[derive(Clone, Copy, Debug, PartialEq, Eq)]
439        pub struct LogFacility: libc::c_int 
440        {
441            /// kernel messages (these can't be generated from user processes)
442            const LOG_KERN = libc::LOG_KERN;
443
444            /// (default) generic user-level messages
445            const LOG_USER = libc::LOG_USER;
446
447            /// mail subsystem
448            const LOG_MAIL = libc::LOG_MAIL;
449
450            /// system daemons without separate facility value
451            const LOG_DAEMON = libc::LOG_DAEMON;
452            
453            /// security/authorization messages
454            const LOG_AUTH = libc::LOG_AUTH;
455
456            /// messages generated internally by syslogd(8)
457            const LOG_SYSLOG = libc::LOG_SYSLOG;
458
459            /// line printer subsystem
460            const LOG_LPR = libc::LOG_LPR;
461
462            /// USENET news subsystem
463            const LOG_NEWS = libc::LOG_NEWS;
464
465            /// UUCP subsystem
466            const LOG_UUCP = libc::LOG_UUCP;
467
468            /// reserved for local use
469            const LOG_LOCAL0 = libc::LOG_LOCAL0;
470
471            /// reserved for local use
472            const LOG_LOCAL1 = libc::LOG_LOCAL1;
473
474            /// reserved for local use
475            const LOG_LOCAL2 = libc::LOG_LOCAL2;
476            
477            /// reserved for local use
478            const LOG_LOCAL3 = libc::LOG_LOCAL3;
479
480            /// reserved for local use
481            const LOG_LOCAL4 = libc::LOG_LOCAL4;
482
483            /// reserved for local use
484            const LOG_LOCAL5 = libc::LOG_LOCAL5;
485
486            /// reserved for local use
487            const LOG_LOCAL6 = libc::LOG_LOCAL6;
488            
489            /// reserved for local use
490            const LOG_LOCAL7 = libc::LOG_LOCAL7;
491        }
492    }
493
494
495    impl FromStr for LogFacility
496    {
497        type Err = SyslogError;
498    
499        fn from_str(s: &str) -> Result<Self, Self::Err> 
500        {
501            let s_up = s.to_ascii_uppercase();
502
503            match s_up.as_str()
504            {
505                "LOG_KERN" => Ok(Self::LOG_KERN),
506                "LOG_USER" => Ok(Self::LOG_USER),
507                "LOG_MAIL" => Ok(Self::LOG_MAIL),
508                "LOG_DAEMON" => Ok(Self::LOG_DAEMON),
509                "LOG_AUTH" => Ok(Self::LOG_AUTH),
510                "LOG_SYSLOG" => Ok(Self::LOG_SYSLOG),
511                "LOG_LPR" => Ok(Self::LOG_LPR),
512                "LOG_NEWS" => Ok(Self::LOG_NEWS),
513                "LOG_UUCP" => Ok(Self::LOG_UUCP),
514                "LOG_LOCAL0" => Ok(Self::LOG_LOCAL0),
515                "LOG_LOCAL1" => Ok(Self::LOG_LOCAL1),
516                "LOG_LOCAL2" => Ok(Self::LOG_LOCAL2),
517                "LOG_LOCAL3" => Ok(Self::LOG_LOCAL3),
518                "LOG_LOCAL4" => Ok(Self::LOG_LOCAL4),
519                "LOG_LOCAL5" => Ok(Self::LOG_LOCAL5),
520                "LOG_LOCAL6" => Ok(Self::LOG_LOCAL6),
521                "LOG_LOCAL7" => Ok(Self::LOG_LOCAL7),
522                n => 
523                    throw_error!("unknwon log facility: {}", n)
524            }
525        }
526    }
527
528    impl fmt::Display for LogFacility
529    {
530        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result 
531        {
532            match *self
533            {
534                Self::LOG_KERN => 
535                    write!(f, "LOG_KERN"),
536                Self::LOG_USER => 
537                    write!(f, "LOG_USER"),
538                Self::LOG_MAIL => 
539                    write!(f, "LOG_MAIL"),
540                Self::LOG_DAEMON => 
541                    write!(f, "LOG_DAEMON"),
542                Self::LOG_AUTH => 
543                    write!(f, "LOG_AUTH"),
544                Self::LOG_SYSLOG => 
545                    write!(f, "LOG_SYSLOG"),
546                Self::LOG_LPR => 
547                    write!(f, "LOG_LPR"),
548                Self::LOG_NEWS => 
549                    write!(f, "LOG_NEWS"),
550                Self::LOG_UUCP => 
551                    write!(f, "LOG_UUCP"),
552                Self::LOG_LOCAL0 => 
553                    write!(f, "LOG_LOCAL0"),
554                Self::LOG_LOCAL1 => 
555                    write!(f, "LOG_LOCAL1"),
556                Self::LOG_LOCAL2 => 
557                    write!(f, "LOG_LOCAL2"),
558                Self::LOG_LOCAL3 => 
559                    write!(f, "LOG_LOCAL3"),
560                Self::LOG_LOCAL4 => 
561                    write!(f, "LOG_LOCAL4"),
562                Self::LOG_LOCAL5 => 
563                    write!(f, "LOG_LOCAL5"),
564                Self::LOG_LOCAL6 => 
565                    write!(f, "LOG_LOCAL6"),
566                Self::LOG_LOCAL7 => 
567                    write!(f, "LOG_LOCAL7"),
568                n =>
569                    write!(f, "LOG_[{}]", n),
570            }
571        }
572    }
573
574    /// Unpriv socket
575    pub const PATH_LOG: &'static str = "/var/run/log";
576
577    /// Priviledged socket
578    pub const PATH_LOG_PRIV: &'static str = "/var/run/logpriv";
579
580    /// backward compatibility
581    pub const PATH_OLDLOG: &'static str = "/dev/log";
582
583    /// OSX compat
584    pub const PATH_OSX: &'static str = "/var/run/syslog";
585
586    /*
587    pub static PATH_CONSOLE: LazyLock<CString> = LazyLock::new(|| 
588        {
589            CString::new("/dev/console").unwrap()
590        }
591    );
592    */
593
594    #[cfg(feature = "build_sync")]
595    pub(crate) mod sync_portion
596    {
597        use std::io::Write;
598        use std::io::IoSlice;
599        use crate::error::SyRes;
600        use crate::map_error_os;
601
602        /// Sends to the FD i.e file of stderr, stdout or any which 
603        /// implements [Write] `write_vectored`.
604        ///
605        /// # Arguments
606        /// 
607        /// * `file_fd` - mutable consume of the container FD.
608        ///
609        /// * `msg` - a reference on array of data
610        ///
611        /// * `newline` - a new line string ref i.e "\n" or "\r\n"
612        pub(crate) 
613        fn send_to_fd<W>(mut file_fd: W, msg: &str, newline: &str) -> SyRes<usize>
614        where W: Write
615        {
616            return 
617                file_fd
618                    .write_vectored(
619                        &[IoSlice::new(msg.as_bytes()), IoSlice::new(newline.as_bytes())]
620                    )
621                    .map_err(|e|
622                        map_error_os!(e, "send_to_fd() writev() failed")
623                    );
624        }
625    }
626
627    use crate::{error::SyslogError, throw_error};
628
629#[cfg(feature = "build_sync")]
630    pub(crate) use self::sync_portion::*;
631
632    #[cfg(test)]
633    mod tests
634    {
635        use super::*;
636
637        #[cfg(feature = "build_sync")]
638        #[test]
639        fn test_error_message()
640        {
641            /*use std::sync::Arc;
642            use std::thread;
643            use std::time::Duration;
644            use super::{LOG_MASK};*/
645
646            let testmsg = "this is test message!";
647            let newline = "\n";
648            let stderr_lock = std::io::stderr().lock();
649            let res = send_to_fd(stderr_lock, testmsg, &newline);
650
651            println!("res: {:?}", res);
652
653            assert_eq!(res.is_ok(), true, "err: {}", res.err().unwrap());
654        
655            return;
656        }
657
658        #[test]
659        fn test_priority_shl()
660        {
661            assert_eq!((1 << 5), (1 << Priority::LOG_NOTICE));
662        }
663    }
664}
665
666/// Path to console.
667pub static PATH_CONSOLE: LazyLock<&Path> = LazyLock::new(|| 
668    {
669        Path::new("/dev/console")
670    }
671);
672
673/// A max dgram init.
674pub static RFC5424_MAX_DGRAM: LazyLock<usize> = LazyLock::new(|| 
675    {
676        portable::get_local_dgram_maxdgram() as usize
677    }
678);
679
680
681/// max hostname size
682pub const MAXHOSTNAMELEN: usize = 256;
683
684/// mask to extract facility part
685pub const LOG_FACMASK: i32 = 0x03f8;
686
687/// Maximum number of characters of syslog message.
688/// According to RFC5424. However syslog-protocol also may state that 
689/// the max message will be defined by the transport layer.
690pub const MAXLINE: usize = 8192;
691
692/// RFC3164 limit
693pub const RFC3164_MAX_PAYLOAD_LEN: usize = 1024;
694
695/// A maximum message which could be passed to Windows Event Log
696pub const WINDOWS_EVENT_REPORT_MAX_PAYLOAD_LEN: usize = 31839;
697
698#[cfg(all(feature = "udp_truncate_1024_bytes", feature = "udp_truncate_1440_bytes"))]
699compile_error!("either 'udp_truncate_1024_bytes' or 'udp_truncate_1440_bytes' should be enabled");
700
701// RFC5424 480 octets or limited by the (transport) MAX_DGRAM_LEN or other.
702#[cfg(feature = "udp_truncate_1024_bytes")]
703pub const RFC5424_UDP_MAX_PKT_LEN: usize  = 1024;
704
705#[cfg(any(feature = "udp_truncate_1440_bytes", all(not(feature = "udp_truncate_1440_bytes"), not(feature = "udp_truncate_1024_bytes"))))]
706pub const RFC5424_UDP_MAX_PKT_LEN: usize  = 2048;
707
708#[cfg(feature = "tcp_truncate_1024_bytes")]
709pub const RFC5424_TCP_MAX_PKT_LEN: usize  = 1024;
710
711#[cfg(feature = "tcp_truncate_2048_bytes")]
712pub const RFC5424_TCP_MAX_PKT_LEN: usize  = 2048;
713
714#[cfg(feature = "tcp_truncate_4096_bytes")]
715pub const RFC5424_TCP_MAX_PKT_LEN: usize  = 4096;
716
717#[cfg(feature = "tcp_truncate_max_bytes")]
718pub const RFC5424_TCP_MAX_PKT_LEN: usize  = MAXLINE;
719
720/// A max byte lenth of APPNAME (NILVALUE / 1*48PRINTUSASCII)
721pub const RFC_MAX_APP_NAME: usize = 48;
722
723/// A private enterprise number.
724pub const IANA_PRIV_ENT_NUM: u64 = 32473;
725
726/// RFC5424 defined value.
727pub const NILVALUE: &'static str = "-";
728
729/// RFC5424 escape character.
730pub const ESC_CHAR_REPL: &'static str = "#000";
731
732/// RFC5424 defined value (bytes).
733pub const NILVALUE_B: &'static [u8] = b"-";
734
735/// White space
736pub const WSPACE: &'static str = " ";
737
738/// Opening brace ('[', ABNF )
739pub const OBRACE: &'static str = "[";
740
741/// Closing brace (']', ABNF %d93)
742pub const CBRACE: &'static str = "]";
743
744/// Closing brace RFC3...
745pub const CBRACE_SEM: &'static str = "]:";
746
747/// Quote-character ('"', ABNF %d34)
748pub const QCHAR: &'static str = "\"";
749
750/// At-sign ("@", ABNF %d64)
751pub const ATSIGN: &'static str = "@";
752
753/// Eq-sign ("=", ABNF %d61)
754pub const EQSIGN: &'static str = "=";
755
756/// A cursor return.
757pub const NEXTLINE: &'static str = "\n";
758
759bitflags! {
760    /// Masks
761    #[derive(Clone, Copy, Debug, PartialEq, Eq)]
762    pub(crate) struct LogMask: i32 
763    {
764        /// A mask for the [LogFacility]
765        const LOG_FACMASK = 0x3f8;
766
767        /// A mask for the [Priority]
768        const LOG_PRIMASK = 7;
769    }
770}
771
772/// A struct which contains syslog encoded Facility and Priority 
773/// is the following order:
774/// 
775/// - log_facility mask 0x3f8
776/// - priority mask 0x7
777#[derive(Debug, Clone, Copy, PartialEq, Eq)]
778pub struct SyslogMsgPriFac(i32);
779
780impl fmt::Display for SyslogMsgPriFac
781{
782    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result 
783    {
784        write!(f, "{}", self.0)
785    }
786}
787
788impl TryFrom<i32> for SyslogMsgPriFac
789{
790    type Error = SyslogError;
791
792    fn try_from(value: i32) -> Result<Self, Self::Error> 
793    {
794        if (value & !(LogMask::LOG_PRIMASK | LogMask::LOG_FACMASK )) != 0
795        {
796            throw_error!("unknwon facility/priority: {:x}", value);
797        }
798
799        return Ok(Self(value));
800    }
801}
802
803impl TryFrom<&[u8]> for SyslogMsgPriFac
804{
805    type Error = SyslogError;
806
807    fn try_from(value: &[u8]) -> Result<Self, Self::Error> 
808    {
809        let val = 
810            str::from_utf8(value)
811                .map_or(
812                    None, 
813                    |pri_str|
814                    {
815                        i32::from_str_radix(pri_str, 10)
816                            .map_or(
817                                None,
818                                |val|
819                                Some(val & LogMask::LOG_PRIMASK | val & LogMask::LOG_FACMASK.bits()) 
820                            )
821                    }
822                )
823                .ok_or(
824                    map_error_code!(InternalError, "cannot convert to prio|facility")
825                )?;
826
827        if (val & !(LogMask::LOG_PRIMASK | LogMask::LOG_FACMASK )) != 0
828        {
829            throw_error!("unknwon facility/priority: {:x}", val);
830        }
831
832        return Ok(Self(val));
833    }
834}
835
836impl SyslogMsgPriFac
837{
838    /// Initializes the instance by adding a priority and facility together
839    /// to form a message header.
840    pub(crate) 
841    fn set_facility(p: Priority, f: LogFacility) -> Self
842    {
843        return Self( p as i32 | f.bits() );
844    }
845
846    /// Returns the raw value.
847    pub 
848    fn get_val(&self) -> i32
849    {
850        return self.0;
851    }
852
853    /// Reads from the inner a [Priority].
854    pub 
855    fn get_priority(&self) -> Priority
856    {
857        return Priority::from(self.0 & LogMask::LOG_PRIMASK);
858    }
859
860    /// Reads from inner a [LogFacility].
861    pub 
862    fn get_log_facility(&self) -> LogFacility
863    {
864        return LogFacility::from_bits_retain(self.0 & LogMask::LOG_FACMASK);
865    }
866}
867
868impl From<i32> for Priority
869{
870    fn from(value: i32) -> Self 
871    {
872        if value == Priority::LOG_ALERT as i32
873        {
874            return Priority::LOG_ALERT;
875        }
876        else if value == Priority::LOG_CRIT as i32
877        {
878            return Priority::LOG_CRIT;
879        }
880        else if value == Priority::LOG_DEBUG as i32
881        {
882            return Priority::LOG_DEBUG;
883        }
884        else if value == Priority::LOG_EMERG as i32
885        {
886            return Priority::LOG_EMERG;
887        }
888        else if value == Priority::LOG_ERR as i32
889        {
890            return Priority::LOG_ERR;
891        }
892        else if value == Priority::LOG_INFO as i32
893        {
894            return Priority::LOG_INFO;
895        }
896        else if value == Priority::LOG_NOTICE as i32
897        {
898            return Priority::LOG_NOTICE;
899        }
900        else
901        {
902            return Priority::LOG_WARNING;
903        }
904    }
905}
906
907/// LOG_MASK is used to create the priority mask in setlogmask. 
908/// For a single Priority mask
909/// used with [Priority]
910/// can be used with | & ! bit operations LOG_MASK()
911///
912/// # Examples
913/// 
914/// ```ignore
915///     LOG_MASK!(Priority::LOG_ALERT) | LOG_MASK!(Priority::LOG_INFO)
916/// ```
917#[macro_export]
918macro_rules! LOG_MASK 
919{
920    ($arg:expr) => (
921        (1 << ($arg))
922    )
923}
924
925/// LOG_MASK is used to create the priority mask in setlogmask
926/// For a mask UPTO specified
927/// used with [Priority]
928///
929/// # Examples
930/// 
931/// ```ignore
932///     LOG_UPTO!(Priority::LOG_ALERT)
933/// ```
934#[macro_export]
935macro_rules! LOG_UPTO 
936{
937    ($arg:expr) => (
938        ((1 << (($arg) + 1)) - 1)
939    )
940}
941
942impl Shl<Priority> for i32
943{
944    type Output = i32;
945
946    fn shl(self, rhs: Priority) -> i32 
947    {
948        let lhs = self;
949        return lhs << rhs as i32;
950    }
951}
952
953impl BitAnd<Priority> for i32
954{
955    type Output = i32;
956
957    #[inline]
958    fn bitand(self, rhs: Priority) -> i32
959    {
960        return self & rhs as i32;
961    }
962}
963
964impl BitAnd<LogMask> for Priority
965{
966    type Output = i32;
967
968    #[inline]
969    fn bitand(self, rhs: LogMask) -> i32
970    {
971        return self as i32 & rhs.bits();
972    }
973}
974
975impl BitAnd<LogMask> for LogFacility 
976{
977    type Output = LogFacility;
978
979    #[inline]
980    fn bitand(self, rhs: LogMask) -> Self::Output
981    {
982        return Self::from_bits_retain(self.bits() & rhs.bits());
983    }
984}
985
986impl BitAnd<LogMask> for i32 
987{
988    type Output = i32;
989
990    #[inline]
991    fn bitand(self, rhs: LogMask) -> i32
992    {
993        return self & rhs.bits();
994    }
995}
996
997/// This function trancated 1 last UTF8 character from the string.
998///
999/// # Arguments
1000///
1001/// * `lt` - a string which is trucated
1002/// 
1003/// # Returns
1004/// 
1005/// * A reference to the ctruncated string
1006pub 
1007fn truncate(lt: &str) -> &str
1008{
1009    let ltt =
1010        match lt.char_indices().nth(lt.len()-1) 
1011        {
1012            None => lt,
1013            Some((idx, _)) => &lt[..idx],
1014        };
1015    return ltt;
1016}
1017
1018/// Trancates the string up to closest to N byte equiv UTF8
1019///  if string exceeds size
1020/// 
1021/// For example:  
1022/// ボルテ 'e3 83 9c e3 83 ab e3 83 86' with N=3  
1023/// will give 'ボ'  
1024/// 
1025/// ボルテ 'e3 83 9c e3 83 ab e3 83 86' with N=4  
1026/// will give 'ボ' 
1027/// 
1028/// ボルテ 'e3 83 9c e3 83 ab e3 83 86' with N=1  
1029/// will give ''
1030/// 
1031/// # Arguments
1032///
1033/// * `lt` - a string to truncate
1034///
1035/// * `n` - a size (in bytes, not in chars)
1036/// 
1037/// # Returns 
1038///
1039/// * A reference to [str] with the time `'t` which corresponds to
1040/// the lifetile of the input argument `'t`.
1041pub 
1042fn truncate_n<'t>(lt: &'t str, n: usize) -> &'t str
1043{
1044    if lt.as_bytes().len() <= n
1045    {
1046        return lt;
1047    }
1048
1049    let mut nn: usize = 0;
1050    let mut cc = lt.chars();
1051    let mut ln: usize;
1052
1053    loop 
1054    {
1055        match cc.next()
1056        {
1057            Some(r) =>
1058            {
1059                ln = r.len_utf8();
1060                nn += ln;
1061
1062                if nn == n
1063                {
1064                    return &lt[..nn];
1065                }
1066                else if nn > n
1067                {
1068                    return &lt[..nn-ln];
1069                }
1070            },
1071            None => 
1072                return lt,
1073        }
1074    }
1075}
1076
1077/// Checks if string are:
1078/// ```text
1079/// NOT EMPTY
1080/// MUST be printable US-ASCII strings, and MUST
1081/// NOT contain an at-sign ('@', ABNF %d64), an equal-sign ('=', ABNF
1082/// %d61), a closing brace (']', ABNF %d93), a quote-character ('"',
1083/// ABNF %d34), whitespace, or control characters
1084/// ```
1085pub
1086fn check_printable(a: &str) -> SyRes<()>
1087{
1088    if a.is_empty() == true
1089    {
1090        throw_error!("empty SD value");
1091    }
1092
1093    for p in a.chars()
1094    {
1095        if p.is_ascii() == false || p.is_ascii_graphic() == false || p == '@' || p == '=' || p == ']' || p == '\"'
1096        {
1097            throw_error!("incorrect char: '{:X}' in SD value", p as u64);
1098        }
1099    }
1100
1101    return Ok(());
1102}
1103
1104
1105pub 
1106fn escape_chars(st: Cow<'static, str>) -> Cow<'static, str>
1107{
1108    let mut out = String::with_capacity(st.len());
1109
1110    for c in st.chars()
1111    {
1112        if c.is_control() == true
1113        {
1114            out.push_str(ESC_CHAR_REPL);
1115        }
1116        else if c == '\"' || c == '\\' || c == ']'
1117        {
1118            out.push('\\');
1119            out.push(c);
1120        }
1121        else
1122        {
1123            out.push(c);
1124        }
1125    }
1126
1127    if st.len() == out.len()
1128    {
1129        return st;
1130    }
1131    else
1132    {
1133        return Cow::Owned(out);
1134    }
1135}
1136
1137
1138#[cfg(test)]
1139mod tests
1140{
1141    use super::*;
1142
1143    #[test]
1144    fn test_truncate()
1145    {
1146        let test = "cat\n";
1147
1148        let trunc = truncate(test);
1149
1150        assert_eq!("cat", trunc);
1151    }
1152
1153
1154
1155    #[test]
1156    fn test_truncate_n()
1157    {
1158        assert_eq!(truncate_n("abcde", 3), "abc");
1159        assert_eq!(truncate_n("ボルテ", 4), "ボ");
1160        assert_eq!(truncate_n("ボルテ", 5), "ボ");
1161        assert_eq!(truncate_n("ボルテ", 6), "ボル");
1162        assert_eq!(truncate_n("abcde", 0), "");
1163        assert_eq!(truncate_n("abcde", 5), "abcde");
1164        assert_eq!(truncate_n("abcde", 6), "abcde");
1165        assert_eq!(truncate_n("ДАТА", 3), "Д");
1166        assert_eq!(truncate_n("ДАТА", 4), "ДА");
1167        assert_eq!(truncate_n("ДАТА", 1), "");
1168    }
1169
1170}