Skip to main content

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, Mutex};
18use std::{str};
19
20use crate::formatters::DefaultSyslogFormatter;
21use crate::sync::{LogItems, SyStream, SyStreamPri, SyStreamSyslogApi};
22use crate::{formatters::SyslogFormatter};
23use crate::{DefaultLocalSyslogDestination, SyslogDestination, common::*};
24use crate::error::SyRes;
25
26
27use crate::sync::syslog_sync_internal::{SyslogSocketLockless};
28
29
30
31/// A `sync`, shared instance of the syslog client which is shared between many
32/// threads. Previously a mutex was used, but since the v5.0.0 a CoW experimental
33/// approach is used. The `CoW` creates clones of the instance without holding
34/// a long mutex locks. When writing, the instance does not hold a long lock on
35/// updated item. Only exclusive lock locks the readers, because the instance is
36/// updated and not usable anyway.
37/// 
38/// If the program has fixed amount of threads, probably the `syslog_threadlocal`
39/// will be better alternative. It has the same functionality, but avoids 
40/// any sync locks by working in current thread.
41/// 
42/// # Traits
43/// 
44/// For this isntance a [SyslogApi] and [SyStreamApi] are implemented.
45/// 
46/// # Examples
47/// 
48/// ```ignore
49/// let log = 
50///     SyncSyslog::openlog(
51///         Some("test1"), 
52///         LogStat::LOG_CONS | LogStat::LOG_NDELAY | LogStat::LOG_PID, 
53///         LogFacility::LOG_DAEMON,
54///         SyslogLocal::new()
55///     );
56/// ```
57/// 
58/// ```ignore
59/// let log = 
60///     SyncSyslog
61///         ::<DefaultSyslogFormatter, SyslogLocal>
62///         ::openlog_with(
63///             Some("test1"), 
64///             LogStat::LOG_CONS | LogStat::LOG_NDELAY | LogStat::LOG_PID, 
65///             LogFacility::LOG_DAEMON,
66///             SyslogLocal::new()
67///         );
68/// ```
69/// 
70/// ```ignore
71/// pub static SYSLOG3: LazyLock<SyncSyslog<DefaultSyslogFormatter, SyslogLocal,>> = 
72///     LazyLock::new(|| 
73///         {
74///             SyncSyslog
75///                 ::<DefaultSyslogFormatter, SyslogLocal>
76///                 ::openlog_with(
77///                     Some("test1"), 
78///                     LogStat::LOG_CONS | LogStat::LOG_NDELAY | LogStat::LOG_PID, 
79///                     LogFacility::LOG_DAEMON,
80///                     SyslogLocal::new()
81///                 )
82///                 .unwrap()
83///         }
84///     );
85/// ```
86/// # Streaming
87/// 
88/// A stream is availble via [SyStreamApi].
89/// 
90/// ```ignore
91/// let _ = write!(SYSLOG.stream(Priority::LOG_DEBUG), "test {} 123 stream test ", d);
92/// ```
93/// 
94/// # Generics
95/// 
96/// * `F` - a [SyslogFormatter] which sets the instance which would 
97///     format the message.
98/// 
99/// * `D` - a [SyslogDestination] instance which is either:
100///     [SyslogLocal], [SyslogFile], [SyslogNet], [SyslogTls]. By
101///     default a `SyslogLocal` is selected.
102#[derive(Debug, Clone)]
103pub struct SyncSyslog<F = DefaultSyslogFormatter, D = DefaultLocalSyslogDestination>
104where 
105    F: SyslogFormatter, 
106    D: SyslogDestination, 
107{   
108    /// An identification i.e program name, thread name
109    log_items: Arc<Mutex<LogItems>>,
110
111    /// A stream (unixdatagram, udp, tcp)
112    stream: Arc<Mutex<SyslogSocketLockless<D>>>,
113
114     _p: PhantomData<F>,
115}
116
117unsafe impl<F: SyslogFormatter, D: SyslogDestination> Send for SyncSyslog<F, D>
118{}
119
120impl SyncSyslog
121{
122    /// Opens a default connection to the local syslog server with default formatter.
123    /// 
124    /// In order to access the syslog API, use the [SyslogApi].
125    /// 
126    /// # Arguments
127    /// 
128    /// * `ident` - A program name which will appear on the logs. If none, will be determined
129    ///     automatically.
130    /// 
131    /// * `logstat` - [LogStat] an instance config.
132    /// 
133    /// * `facility` - [LogFacility] a syslog facility.
134    /// 
135    /// * `net_tap_prov` - a [SyslogLocal] instance with configuration.
136    /// 
137    /// # Returns
138    /// 
139    /// A [SyRes] is returned ([Result]) with: 
140    /// 
141    /// * [Result::Ok] - with instance
142    /// 
143    /// * [Result::Err] - with error description.
144    pub 
145    fn openlog(ident: Option<&str>, logstat: LogStat, facility: LogFacility, 
146        net_tap_prov: DefaultLocalSyslogDestination) -> SyRes<Self> 
147    {
148        return Ok( 
149            Self
150            {
151                log_items: 
152                    Arc::new(
153                        Mutex::new(
154                            LogItems::new(ident, 0xff, logstat, facility)
155                        )
156                    ),
157                stream: 
158                    Arc::new(
159                        Mutex::new(
160                            SyslogSocketLockless::<DefaultLocalSyslogDestination>::new(logstat, net_tap_prov)?
161                        )
162                    ),
163                _p: 
164                    PhantomData,
165            }
166        );
167    }
168}
169
170
171impl<F: SyslogFormatter, D: SyslogDestination> SyncSyslog<F, D>
172{
173    /// Opens a special connection to the destination syslog server with specific formatter.
174    /// 
175    /// All struct generic should be specified before calling this function.
176    /// 
177    /// In order to access the syslog API, use the [SyslogApi].
178    /// 
179    /// # Arguments
180    /// 
181    /// * `ident` - A program name which will appear on the logs. If none, will be determined
182    ///     automatically.
183    /// 
184    /// * `logstat` - [LogStat] an instance config.
185    /// 
186    /// * `facility` - [LogFacility] a syslog facility.
187    /// 
188    /// * `net_tap_prov` - a destination server. A specific `D` instance which contains infomation 
189    ///     about the destination server. See `syslog_provider.rs`.
190    /// 
191    /// # Returns
192    /// 
193    /// A [SyRes] is returned ([Result]) with: 
194    /// 
195    /// * [Result::Ok] - with instance
196    /// 
197    /// * [Result::Err] - with error description.
198    pub 
199    fn openlog_with(ident: Option<&str>, logstat: LogStat, facility: LogFacility, net_tap_prov: D) -> SyRes<Self> 
200    {
201        return Ok( 
202            Self
203            {
204                log_items: 
205                    Arc::new(
206                        Mutex::new(
207                            LogItems::new(ident, 0xff, logstat, facility)
208                        )
209                    ),
210                stream: 
211                    Arc::new(
212                        Mutex::new(
213                            SyslogSocketLockless::<D>::new(logstat, net_tap_prov)?
214                        )
215                    ),
216                _p: 
217                    PhantomData,
218            }
219        );
220    }
221}
222
223
224impl<F: SyslogFormatter, D: SyslogDestination> SyncSyslog<F, D>
225{
226    /// Connects the current instance to the syslog server (destination).
227    pub 
228    fn connectlog(&self) -> SyRes<()>
229    {
230        return 
231            self
232                .stream
233                .lock()
234                .unwrap()
235                .connectlog();
236    }
237
238    /// Sets the logmask to filter out the syslog calls.
239    /// 
240    /// See macroses [LOG_MASK] and [LOG_UPTO] to generate mask
241    ///
242    /// # Example
243    ///
244    /// LOG_MASK!(Priority::LOG_EMERG) | LOG_MASK!(Priority::LOG_ERROR)
245    ///
246    /// or
247    ///
248    /// ~(LOG_MASK!(Priority::LOG_INFO))
249    /// LOG_UPTO!(Priority::LOG_ERROR)
250    pub 
251    fn setlogmask(&self, logmask: i32) -> SyRes<i32> 
252    {
253        let pri = 
254            self
255                .log_items
256                .lock()
257                .unwrap()
258                .set_logmask(logmask);
259
260        return Ok(pri);
261    }
262
263    /// Closes connection to the syslog server (destination).
264    pub 
265    fn closelog(&self) -> SyRes<()> 
266    {
267        return 
268            self
269                .stream
270                .lock()
271                .unwrap()
272                .disconnectlog();
273    }
274
275    /// Similar to libc, syslog() sends data to syslog server.
276    /// 
277    /// # Arguments
278    ///
279    /// * `pri` - a priority [Priority]
280    ///
281    /// * `fmt` - a formatter [SyslogFormatter] message. In C exists a functions with
282    ///     variable argumets amount. In Rust you should create your
283    ///     own macros like format!() or use format!()]. The [String] and ref `'static` 
284    ///     [str] can be passed directly.
285    /// 
286    /// # Returns 
287    /// 
288    /// A [SyRes] is returned which may describe an error.
289    #[inline]
290    pub 
291    fn syslog(&self, pri: Priority, fmt: F) -> SyRes<()>
292    {
293        let Some((formatted_msg, logstat)) = 
294            self.log_items.lock().unwrap().vsyslog1_msg::<F, D>(pri, &fmt)
295        else { return Ok(()) };
296
297        self.stream.lock().unwrap().vsyslog1(logstat, formatted_msg)
298    }
299
300    /// This function can be used to update the facility name, for example
301    /// after fork().
302    /// 
303    /// # Arguments
304    /// 
305    /// * `ident` - an [Option] optional new identity (up to 48 UTF8 chars)
306    ///     If set to [Option::None] would request the program name from OS.
307    pub 
308    fn change_identity(&self, ident: Option<&str>) -> SyRes<()>
309    {
310        self
311            .log_items
312            .lock()
313            .unwrap()
314            .set_identity(ident);
315
316        return Ok(());
317    }
318
319    /// Re-opens the connection to the syslog server. Can be used to 
320    /// rotate logs(handle SIGHUP).
321    /// 
322    /// # Returns
323    /// 
324    /// A [Result] is retured as [SyRes].
325    /// 
326    /// * [Result::Ok] - with empty inner type.
327    /// 
328    /// * [Result::Err] - an error code and description 
329    pub 
330    fn reconnect(&self) -> SyRes<()>
331    {
332        return
333            self
334                .stream
335                .lock()
336                .unwrap()
337                .reconnectlog();
338    }
339
340    /// Updates the instance's socket. `tap_data` [TapTypeData] should be of
341    /// the same variant (type) as current.
342    pub 
343    fn update_tap_data(&self, tap_data: D) -> SyRes<()>
344    {
345        return
346            self
347                .stream
348                .lock()
349                .unwrap()
350                .update_tap_data(tap_data);
351    }
352
353}
354
355impl<F, D> SyncSyslog<F, D>
356where F: SyslogFormatter, D: SyslogDestination
357{
358    /// Returns the streamable [SyStream] instance which can be used with [write!].
359    /// 
360    /// It implements both [std::fmt::Write] and [std::io::Write].
361    /// 
362    /// # Example
363    /// 
364    /// ```ignore
365    /// let log = 
366    ///     SingleSyslog::openlog(
367    ///         Some("test1"), 
368    ///         LogStat::LOG_CONS | LogStat::LOG_NDELAY | LogStat::LOG_PID, 
369    ///         LogFacility::LOG_DAEMON,
370    ///         SyslogLocal::new()
371    ///     ).unwrap();
372    /// 
373    /// write!(log.get_stream::<SyStreamPriDebug>(), "test stream singlesyslog {}", i).unwrap();
374    /// ```
375    pub 
376    fn get_stream<'t, PRI>(&'t self) -> SyStream<'t, PRI, D, F, &'t Self>
377    where PRI: SyStreamPri
378    {
379        SyStream
380        {
381            s: Some(self),
382            _p: PhantomData,
383            _p1: PhantomData,
384            _p2: PhantomData
385        }
386    }
387}
388
389
390impl<F: SyslogFormatter, D: SyslogDestination> SyStreamSyslogApi<F, D>  
391for &SyncSyslog<F, D>
392{
393    type SYSLOG<'t> = &'t SyncSyslog<F, D>;
394
395    fn syslog<'t>(syslog: Self::SYSLOG<'t>, pri: Priority, fmt: F) -> SyRes<()>
396    {
397        syslog.syslog(pri, fmt)
398    }
399}
400
401