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