syslog_rs/sync/
syslog_sync_shared.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
16use std::marker::PhantomData;
17use std::sync::Arc;
18use std::{str};
19
20use instance_copy_on_write::ICoW;
21
22use crate::formatters::DefaultSyslogFormatter;
23use crate::sync::syslog_stream::SyStreamApi;
24use crate::sync::syslog_sync_internal::{SyslogSocket};
25use crate::sync::{LogItems, SyStream};
26use crate::{formatters::SyslogFormatter, map_error_code};
27use crate::{common::*, SyslogDestination, SyslogLocal};
28use crate::error::SyRes;
29
30use super::syslog_trait::SyslogApi;
31
32
33/// A sync, shared instance of the syslog client which is shared between
34/// threads. Previously a mutex was used, but since the v5.0.0 a CoW experimental
35/// approach is used.
36/// 
37/// For this isntance a [SyslogApi] and [SyStreamApi] are implemented.
38/// 
39/// ```ignore
40/// let log = 
41///     SyncSyslog::openlog(
42///         Some("test1"), 
43///         LogStat::LOG_CONS | LogStat::LOG_NDELAY | LogStat::LOG_PID, 
44///         LogFacility::LOG_DAEMON,
45///         SyslogLocal::new()
46///     );
47/// ```
48/// 
49/// ```ignore
50/// let log = 
51///     SyncSyslog
52///         ::<DefaultSyslogFormatter, SyslogLocal>
53///         ::openlog_with(
54///             Some("test1"), 
55///             LogStat::LOG_CONS | LogStat::LOG_NDELAY | LogStat::LOG_PID, 
56///             LogFacility::LOG_DAEMON,
57///             SyslogLocal::new()
58///         );
59/// ```
60/// 
61/// ```ignore
62/// pub static SYSLOG3: LazyLock<SyncSyslog<DefaultSyslogFormatter, SyslogLocal,>> = 
63///     LazyLock::new(|| 
64///         {
65///             SyncSyslog
66///                 ::<DefaultSyslogFormatter, SyslogLocal>
67///                 ::openlog_with(
68///                     Some("test1"), 
69///                     LogStat::LOG_CONS | LogStat::LOG_NDELAY | LogStat::LOG_PID, 
70///                     LogFacility::LOG_DAEMON,
71///                     SyslogLocal::new()
72///                 )
73///                 .unwrap()
74///         }
75///     );
76/// ```
77/// 
78/// A stream is availble via [SyStreamApi].
79/// 
80/// ```ignore
81/// let _ = write!(SYSLOG.stream(Priority::LOG_DEBUG), "test {} 123 stream test ", d);
82/// ```
83/// 
84/// # Generics
85/// 
86/// * `F` - a [SyslogFormatter] which sets the instance which would 
87///     format the message.
88/// 
89/// * `D` - a [SyslogDestination] instance which is either:
90///     [SyslogLocal], [SyslogFile], [SyslogNet], [SyslogTls]. By
91///     default a `SyslogLocal` is selected.
92#[derive(Debug, Clone)]
93pub struct SyncSyslog<F = DefaultSyslogFormatter, D = SyslogLocal>
94where 
95    F: SyslogFormatter, 
96    D: SyslogDestination, 
97{   
98    /// An identification i.e program name, thread name
99    log_items: Arc<ICoW<LogItems>>,
100
101    /// A stream (unixdatagram, udp, tcp)
102    stream: Arc<SyslogSocket<D>>,
103
104     _p: PhantomData<F>,
105}
106
107unsafe impl<F: SyslogFormatter, D: SyslogDestination> Send for SyncSyslog<F, D>
108{}
109
110impl SyncSyslog
111{
112    /// Opens a default connection to the local syslog server with default formatter.
113    /// 
114    /// In order to access the syslog API, use the [SyslogApi].
115    /// 
116    /// # Arguments
117    /// 
118    /// * `ident` - A program name which will appear on the logs. If none, will be determined
119    ///     automatically.
120    /// 
121    /// * `logstat` - [LogStat] an instance config.
122    /// 
123    /// * `facility` - [LogFacility] a syslog facility.
124    /// 
125    /// * `net_tap_prov` - a [SyslogLocal] instance with configuration.
126    /// 
127    /// # Returns
128    /// 
129    /// A [SyRes] is returned ([Result]) with: 
130    /// 
131    /// * [Result::Ok] - with instance
132    /// 
133    /// * [Result::Err] - with error description.
134    pub 
135    fn openlog(ident: Option<&str>, logstat: LogStat, facility: LogFacility, net_tap_prov: SyslogLocal) -> SyRes<Self> 
136    {
137        return Ok( 
138            Self
139            {
140                log_items: 
141                    Arc::new(
142                        ICoW::new(
143                            LogItems::new(ident, 0xff, logstat, facility)
144                        )
145                    ),
146                stream: 
147                    Arc::new(
148                        SyslogSocket::<SyslogLocal>::new(logstat, net_tap_prov)?
149                    ),
150                _p: 
151                    PhantomData,
152            }
153        );
154    }
155}
156
157
158impl<F: SyslogFormatter, D: SyslogDestination> SyncSyslog<F, D>
159{
160    /// Opens a special connection to the destination syslog server with specific formatter.
161    /// 
162    /// All struct generic should be specified before calling this function.
163    /// 
164    /// In order to access the syslog API, use the [SyslogApi].
165    /// 
166    /// # Arguments
167    /// 
168    /// * `ident` - A program name which will appear on the logs. If none, will be determined
169    ///     automatically.
170    /// 
171    /// * `logstat` - [LogStat] an instance config.
172    /// 
173    /// * `facility` - [LogFacility] a syslog facility.
174    /// 
175    /// * `net_tap_prov` - a destination server. A specific `D` instance which contains infomation 
176    ///     about the destination server. See `syslog_provider.rs`.
177    /// 
178    /// # Returns
179    /// 
180    /// A [SyRes] is returned ([Result]) with: 
181    /// 
182    /// * [Result::Ok] - with instance
183    /// 
184    /// * [Result::Err] - with error description.
185    pub 
186    fn openlog_with(ident: Option<&str>, logstat: LogStat, facility: LogFacility, net_tap_prov: D) -> SyRes<Self> 
187    {
188        return Ok( 
189            Self
190            {
191                log_items: 
192                    Arc::new(
193                        ICoW::new(
194                            LogItems::new(ident, 0xff, logstat, facility)
195                        )
196                    ),
197                stream: 
198                    Arc::new(
199                        SyslogSocket::<D>::new(logstat, net_tap_prov)?
200                    ),
201                _p: 
202                    PhantomData,
203            }
204        );
205    }
206}
207
208
209impl<F: SyslogFormatter, D: SyslogDestination> SyslogApi<F, D> for SyncSyslog<F, D>
210{
211    fn connectlog(&self) -> SyRes<()>
212    {
213        return 
214            self
215                .stream
216                .connectlog();
217    }
218
219    fn setlogmask(&self, logmask: i32) -> SyRes<i32> 
220    {
221        let mut transaction = 
222            self
223                .log_items
224                .as_ref()
225                .clone();
226                /*.map_err(|e|
227                    map_error_code!(CoWExclLock, "set logmask failed with: {}", e)
228                )?;*/
229
230        let pri = 
231            transaction
232                .set_logmask(logmask);
233
234        transaction
235            .commit()
236            .map_err(|e|
237                map_error_code!(CoWWriteError, "set logmask failed with: {}", e.0)
238            )?;
239
240        return Ok(pri);
241    }
242
243    fn closelog(&self) -> SyRes<()> 
244    {
245        return 
246            self
247                .stream
248                .disconnectlog();
249    }
250
251    #[inline]
252    fn syslog(&self, pri: Priority, fmt: F) 
253    {
254        let Some((formatted_msg, logstat)) = 
255            self.log_items.read().vsyslog1_msg::<F, D>(pri, &fmt)
256            else { return };
257
258        self.stream.vsyslog1(logstat, formatted_msg);
259
260        return;
261    }
262
263    /// This function can be used to update the facility name, for example
264    /// after fork().
265    /// 
266    /// # Arguments
267    /// 
268    /// * `ident` - a new identity (up to 48 UTF8 chars)
269    fn change_identity(&self, ident: Option<&str>) -> SyRes<()>
270    {
271        let mut clonned = 
272            self
273                .log_items
274                .as_ref()
275                .clone();
276                /*.map_err(|e|
277                    map_error_code!(CowTransactionFailed, "change_identity to '{:?}' was not completed due to error: '{}'", ident, e)
278                )?;*/
279
280        clonned.set_identity(ident);
281
282        clonned
283            .commit()
284            .map_err(|e|
285                map_error_code!(CoWWriteError, "cannot change_identity due to exclusive lock, err: '{}'", e.0)
286            )?;
287
288        return Ok(());
289    }
290
291    fn reconnect(&self) -> SyRes<()>
292    {
293        return
294            self
295                .stream
296                .reconnectlog();
297    }
298
299    fn update_tap_data(&self, tap_data: D) -> SyRes<()>
300    {
301        return
302            self
303                .stream
304                .update_tap_data(tap_data);
305    }
306
307}
308
309impl<'stream, F: SyslogFormatter, D: SyslogDestination> SyStreamApi<'stream, F, D, SyncSyslog<F, D>> 
310for SyncSyslog<F, D>
311{
312    fn stream(&'stream self, pri: Priority) -> SyStream<'stream, D, F, SyncSyslog<F, D>> 
313    {
314        return 
315            SyStream 
316            { 
317                inner: self, 
318                pri: pri, 
319                _p: PhantomData, 
320                _p1: PhantomData 
321            };
322    }
323}
324
325#[cfg(test)]
326mod tests
327{
328    use crate::
329    {
330       error::SyslogErrCode, LogFacility, LogStat, Priority, SyStreamApi, SyncSyslog, SyslogApi, SyslogLocal 
331    };
332
333    #[test]
334    fn test_single_message()
335    {
336
337        let log = 
338            SyncSyslog::openlog(
339                Some("test1"), 
340                LogStat::LOG_CONS | LogStat::LOG_NDELAY | LogStat::LOG_PID, 
341                LogFacility::LOG_DAEMON,
342            SyslogLocal::new()
343        );
344
345        assert_eq!(log.is_ok(), true, "{}", log.err().unwrap());
346
347        let log = log.unwrap();
348
349        let msg1 = format!("test UTF-8 проверка BOM UTF-8");
350        let now = std::time::Instant::now();
351
352        log.syslog(Priority::LOG_DEBUG, msg1.into());
353
354        let dur = now.elapsed();
355        println!("{:?}", dur);
356
357        let msg2 = format!("test UTF-8  きるさお命泉ぶねりよ日子金れっ");
358
359        let now = std::time::Instant::now();
360
361        log.syslog(Priority::LOG_DEBUG, msg2.into());
362
363        let dur = now.elapsed();
364        println!("{:?}", dur);
365
366        let _ = log.closelog();
367
368        return;
369    }
370
371    #[test]
372    fn test_single_stream_test()
373    {
374        use std::fmt::Write;
375
376        let log = 
377            SyncSyslog::openlog(
378                Some("test1"), 
379                LogStat::LOG_CONS | LogStat::LOG_NDELAY | LogStat::LOG_PID, 
380                LogFacility::LOG_DAEMON,
381            SyslogLocal::new());
382
383        assert_eq!(log.is_ok(), true, "{}", log.err().unwrap());
384
385        let log = log.unwrap();
386        
387        write!(log.stream(Priority::LOG_DEBUG), "test123").unwrap();
388
389    }
390
391    #[test]
392    fn test_single_message_withound_nd()
393    {
394
395        let log = 
396            SyncSyslog::openlog(
397                Some("test1_nond"), 
398                LogStat::LOG_CONS | LogStat::LOG_PID, 
399                LogFacility::LOG_DAEMON,
400            SyslogLocal::new());
401
402        assert_eq!(log.is_ok(), true, "{}", log.err().unwrap());
403
404        let log = log.unwrap();
405
406        let msg1 = format!("test UTF-8 проверка BOM UTF-8");
407        let now = std::time::Instant::now();
408
409        log.syslog(Priority::LOG_DEBUG, msg1.into());
410
411        let dur = now.elapsed();
412        println!("{:?}", dur);
413
414        let msg2 = format!("test UTF-8  きるさお命泉ぶねりよ日子金れっ");
415
416        let now = std::time::Instant::now();
417
418        log.syslog(Priority::LOG_DEBUG, msg2.into());
419
420        let dur = now.elapsed();
421        println!("{:?}", dur);
422
423        let _ = log.closelog();
424
425        return;
426    }
427
428    #[test]
429    fn test_single_message_perror()
430    {
431        /*use std::sync::Arc;
432        use std::thread;
433        use std::time::Duration;
434        use super::{LOG_MASK};*/
435
436        let log = 
437            SyncSyslog::openlog(
438                    Some("test2"), 
439                    LogStat::LOG_CONS | LogStat::LOG_NDELAY | LogStat::LOG_PID | LogStat::LOG_PERROR, 
440                    LogFacility::LOG_DAEMON,
441                    SyslogLocal::new()
442                );
443
444        assert_eq!(log.is_ok(), true, "{}", log.err().unwrap());
445
446        let log = log.unwrap();
447
448        log.syslog(Priority::LOG_DEBUG, format!("perror test UTF-8 きるさお命泉ぶねりよ日子金れっ проверка BOM").into());
449
450        let _ = log.closelog();
451
452        return;
453    }
454
455    #[test]
456    fn test_multithreading()
457    {
458        use std::sync::Arc;
459        use std::thread;
460        use std::time::{Instant, Duration};
461
462        let log = 
463                SyncSyslog::openlog(
464                    Some("test3"), 
465                    LogStat::LOG_CONS | LogStat::LOG_NDELAY | LogStat::LOG_PID, 
466                    LogFacility::LOG_DAEMON,
467                SyslogLocal::new()
468            );
469
470        assert_eq!(log.is_ok(), true, "{}", log.err().unwrap());
471
472        let log = Arc::new(log.unwrap());
473        let c1_log = log.clone();
474        let c2_log = log.clone();
475
476        thread::spawn(move|| {
477            for i in 0..5
478            {
479                //thread::sleep(Duration::from_nanos(200));
480                let fmmt = format!("a message from thread 1 #{}[]", i);
481                let now = Instant::now();
482                c1_log.syslog(Priority::LOG_DEBUG, fmmt.into());
483                let elapsed = now.elapsed();
484                println!("t1: {:?}", elapsed);
485            }
486        });
487
488        thread::spawn(move|| {
489            for i in 0..5
490            {
491                //thread::sleep(Duration::from_nanos(201));
492                let fmmt = format!("きるさお命泉ぶねりよ日子金れっ {}", i);
493                let now = Instant::now();
494                c2_log.syslog(Priority::LOG_DEBUG, fmmt.into());
495                let elapsed = now.elapsed();
496                println!("t2: {:?}", elapsed);
497            }
498        });
499
500        let now = Instant::now();
501        log.syslog(Priority::LOG_DEBUG, format!("A message from main, きるさお命泉ぶねりよ日子金れっ").into());
502        let elapsed = now.elapsed();
503        println!("main: {:?}", elapsed);
504
505        thread::sleep(Duration::from_secs(2));
506
507        let _ = log.closelog();
508
509        return;
510    }
511
512    #[test]
513    fn test_multithreading_rw()
514    {
515        use std::sync::Arc;
516        use std::thread;
517        use std::time::{Instant, Duration};
518
519        let log = 
520                SyncSyslog::openlog(
521                    Some("test3"), 
522                    LogStat::LOG_CONS | LogStat::LOG_NDELAY | LogStat::LOG_PID, 
523                    LogFacility::LOG_DAEMON,
524                SyslogLocal::new()
525            );
526
527        assert_eq!(log.is_ok(), true, "{}", log.err().unwrap());
528
529        let log = Arc::new(log.unwrap());
530        let c1_log = log.clone();
531
532        let thread_handler = 
533            thread::spawn(move|| 
534                {
535                    std::thread::park();
536                    for i in 0..30
537                    {
538                        
539
540                        let fmmt = format!("a message from thread 1 #{}[]", i);
541                        let now = Instant::now();
542                        c1_log.syslog(Priority::LOG_DEBUG, fmmt.into());
543                        let elapsed = now.elapsed();
544                        println!("t1: {:?}", elapsed);
545                    }
546                }
547            );
548
549        let now = Instant::now();
550        log.syslog(Priority::LOG_DEBUG, format!("A message from main, きるさお命泉ぶねりよ日子金れっ").into());
551        let elapsed = now.elapsed();
552        println!("main: {:?}", elapsed);
553
554        thread_handler.thread().unpark();
555
556        for i in 0..3
557        {
558            std::thread::sleep(Duration::from_nanos(999));
559            let identity = format!("identity{}", i);
560            let now = Instant::now();
561            log.change_identity(Some(&identity)).unwrap();
562            let elapsed = now.elapsed();
563            println!("idc: {:?}", elapsed);
564        }
565
566        thread_handler.join().unwrap();
567
568        let _ = log.closelog();
569
570        return;
571    }
572
573    #[test]
574    fn test_multithreading_rw_sock()
575    {
576        use std::sync::Arc;
577        use std::thread;
578        use std::time::{Instant, Duration};
579
580        let log = 
581                SyncSyslog::openlog(
582                    Some("test3"), 
583                    LogStat::LOG_CONS | LogStat::LOG_NDELAY | LogStat::LOG_PID, 
584                    LogFacility::LOG_DAEMON,
585                SyslogLocal::new()
586            );
587
588        assert_eq!(log.is_ok(), true, "{}", log.err().unwrap());
589
590        let log = Arc::new(log.unwrap());
591        let c1_log = log.clone();
592
593        let thread_handler = 
594            thread::spawn(move|| 
595                {
596                    std::thread::park();
597                    for i in 0..30
598                    {
599                        
600
601                        let fmmt = format!("a message from thread 1 #{}[]", i);
602                        let now = Instant::now();
603                        c1_log.syslog(Priority::LOG_DEBUG, fmmt.into());
604                        let elapsed = now.elapsed();
605                        println!("t1: {:?}", elapsed);
606                    }
607                }
608            );
609
610        let now = Instant::now();
611        log.syslog(Priority::LOG_DEBUG, format!("A message from main, きるさお命泉ぶねりよ日子金れっ").into());
612        let elapsed = now.elapsed();
613        println!("main: {:?}", elapsed);
614
615        thread_handler.thread().unpark();
616
617        for _ in 0..3
618        {
619            std::thread::sleep(Duration::from_nanos(999));
620            let now = Instant::now();
621            log.update_tap_data(SyslogLocal::new()).unwrap();
622            let elapsed = now.elapsed();
623            println!("idc: {:?}", elapsed);
624        }
625
626        thread_handler.join().unwrap();
627
628        let _ = log.closelog();
629
630        return;
631    }
632
633    #[test]
634    fn test_multithreading_simlock()
635    {
636        use std::sync::Arc;
637        use std::thread;
638        use std::time::Instant;
639
640        let log = 
641                SyncSyslog::openlog(
642                    Some("test3"), 
643                    LogStat::LOG_CONS | LogStat::LOG_NDELAY | LogStat::LOG_PID, 
644                    LogFacility::LOG_DAEMON,
645                SyslogLocal::new()
646            );
647
648        assert_eq!(log.is_ok(), true, "{}", log.err().unwrap());
649
650        let log = Arc::new(log.unwrap());
651        for _ in 0..5
652        {
653            let c1_log = log.clone();
654
655            let thread_handler = 
656                thread::spawn(move || 
657                    {
658                        std::thread::park();
659                        let res = c1_log.update_tap_data(SyslogLocal::new());
660
661                        return res;
662                    }
663                );
664
665            let now = Instant::now();
666            log.syslog(Priority::LOG_DEBUG, format!("A message from main, きるさお命泉ぶねりよ日子金れっ").into());
667            let elapsed = now.elapsed();
668            println!("main: {:?}", elapsed);
669
670            thread_handler.thread().unpark();
671
672            let res = log.update_tap_data(SyslogLocal::new());
673
674            let res2 = thread_handler.join().unwrap();
675
676            if res.is_ok() == true && res2.is_ok() == true
677            {
678                continue;
679            }
680            else if res.is_err() == true
681            {
682                println!("res err: {}", res.as_ref().err().unwrap());
683                assert_eq!(res.err().unwrap().get_errcode(), SyslogErrCode::CoWAlreadyLocked);
684            }
685            else if res2.is_err() == true
686            {
687                println!("res2 err: {}", res2.as_ref().err().unwrap());
688                assert_eq!(res2.err().unwrap().get_errcode(), SyslogErrCode::CoWAlreadyLocked);
689            }
690
691            return;
692        }
693
694        panic!("faild!");
695    }
696
697    #[test]
698    fn long_msg_test()
699    {
700        // 40 EMSGSIZE
701        let msg = 
702    "7赤ざクぽな載覧な改申ほふ取容う主坊酸ヱ司戦ヒソオ力端めゃ間真ル実記キ団需くスルて学回県仁京ぴ熱完かあもく。上ょぜ催5強変却ソヲキ転入ク記事購シリ断衝ぽ玲面たぽつへ様態の善無かー勢加ヨマナキ趣会撮さはこ違42器のレうゆ。稿エスタ己報照アイネル岩撲ムニ半者サアキツ画47込死請誌8策ノ再然ださそ禁断しにた高否リ続最ユケ山芸ロ去群づへ索芭あン掲佳怖斎澤クね。
703    書キマコヒ上広ざばてわ会佐のにりリ学総点フわすか頼野ぜイよば株約ネキヱメ必軽チヲ録使ラ下能ナウコ紀捜れ霊別イ摯療じらどト相3聞めびら情拳ユフエ確経板植えーぜあ。無ど曲注程クタル系新どばくい都面村リと計安ワヱ月見モヌエノ完臨健85定テサコミ貸容ミテタカ写載とかえげ詐訃だみそな。代け設養テヨナ乱54服料ニ止画ら不暮ッ強治ぱンやし供方広づじもあ般判ごくドラ示刊ヱサトキ速事取さふぞ授共西れッ。
704    陸オク康59面くこ惑7伊エノモ候余ロソウ政投ナク転文だけス香両4誕よ数真ひぞぴざ空加ユラ勢隣ゃびよ移72士シヌ図際想カマテ覧費活輸権因ん。東がイ価変や濃恒ヒメネエ学見ょ理供カオムヱ針中わば文63転ヨリホミ礼民ネトマツ速果還ばが転世晴げでんが。犯レト同岸サケ写新ゆぐぱひ人捕けほゅえ的人第いぜ転連ぎど京分株ニムトヒ家及ユフケヌ記定み歩死質をゃいイ都末ワ革量んつ覧打百レいん。
705    下ゃとぞら川株以東ぴそぱ費更ウマヨメ覧論ウスレモ八度ずんとイ発平う果構つ小各ぼス夢国フラ連石はちい雪飲聴ゅいと。門ぱし続消上ト文影投設テユチ設川ルラシケ引押ヤマセメ権書ハテ例発リロソイ環載ぴ企実カマリト店鉄ワソロフ逆子先チルマ楽法どゆね声東女携抜爆んけみ。14報ふラめて怒止に開謙こさ促大ヘチツ遺飯ケ営長コキ価空少ろッひぱ読信ケテウ者育ヒミク代57柱浪フ。
706    ";
707
708    println!("{}", msg.len());
709
710        let log = 
711                SyncSyslog::openlog(
712                    Some("test4"), 
713                    LogStat::LOG_CONS | LogStat::LOG_NDELAY | LogStat::LOG_PID, 
714                    LogFacility::LOG_DAEMON,
715                    SyslogLocal::new()
716                );
717
718        assert_eq!(log.is_ok(), true, "{}", log.err().unwrap());
719
720        let log = log.unwrap();
721
722        log.syslog(Priority::LOG_DEBUG, format!("{}", msg).into());
723                    
724        let _ = log.closelog();
725
726        return;
727    }
728}