1use std::borrow::Cow;
16use std::marker::PhantomData;
17use std::thread::sleep;
18use std::time::Duration;
19
20use nix::libc;
21
22
23use crate::formatters::SyslogFormatter;
24use crate::map_error_os;
25use crate::portable;
26use crate::common::*;
27use crate::error::{SyRes};
28use crate::socket::TapType;
29use crate::SyslogDestination;
30
31use super::socket::*;
32
33#[derive(Debug)]
35pub struct SyncSyslogInternal<F: SyslogFormatter, D: SyslogDestination>
36{
37 logtag: String,
39
40 logpid: String,
42
43 logstat: LogStat,
45
46 facility: LogFacility,
48
49 logmask: i32,
51
52 stream: D::SocketTap,
54
55 _p: PhantomData<F>,
56}
57
58
59impl<F: SyslogFormatter, D: SyslogDestination> Drop for SyncSyslogInternal<F, D>
60{
61 fn drop(&mut self)
62 {
63 let _ = self.disconnectlog();
65 }
66}
67
68impl<F: SyslogFormatter, D: SyslogDestination> SyncSyslogInternal<F, D>
71{
72 pub(crate)
95 fn new(
96 ident: Option<&str>,
97 logstat: LogStat,
98 facility: LogFacility,
99 req_tap: D
100 ) -> SyRes<Self>
101 {
102 let log_facility =
104 if facility.is_empty() == false &&
105 (facility & !LogMask::LOG_FACMASK).is_empty() == true
106 {
107 facility
108 }
109 else
110 {
111 LogFacility::LOG_USER
113 };
114
115 let logtag =
116 match ident
117 {
118 Some(r) =>
119 truncate_n(r, RFC_MAX_APP_NAME).to_string(),
120 None =>
121 truncate_n(
122 portable::p_getprogname()
123 .unwrap_or("".to_string())
124 .as_str(),
125 RFC_MAX_APP_NAME
126 )
127 .to_string()
128 };
129
130 let sock = D::SocketTap::new(req_tap)?;
131
132 return Ok(
133 Self
134 {
135 logtag: logtag,
136 logpid: portable::get_pid().to_string(),
137 logstat: logstat,
138 facility: log_facility,
139 logmask: 0xff,
140 stream: sock,
141 _p: PhantomData
142 }
143 );
144 }
145
146 pub(crate)
148 fn update_tap_data(&mut self, tap_data: D) -> SyRes<()>
149 {
150 let is_con = self.stream.is_connected();
151
152 if is_con == true
153 {
154 self
155 .stream
156 .disconnectlog()
157 .map_err(|e|
158 map_error_os!(e, "update_tap_data() can not disconnect log properly")
159 )?;
160 }
161
162 self.stream.update_tap_data(tap_data);
163
164 if is_con == true
165 {
166 self.stream.connectlog()?;
168 }
169
170 return Ok(());
171 }
172
173 #[inline]
174 pub(super)
175 fn send_to_stderr(&self, msg: &[Cow<'_, str>])
176 {
177 if self.logstat.intersects(LogStat::LOG_PERROR) == true
178 {
179 let stderr_lock = std::io::stderr().lock();
180 let newline = "\n";
181
182 let _ = send_to_fd(stderr_lock, msg, &newline);
183 }
184 }
185
186 #[inline]
187 fn send_to_syscons(&self, msg_payload: &[Cow<'_, str>])
188 {
189 use std::fs::File;
190 use std::os::unix::fs::OpenOptionsExt;
191
192 if self.logstat.intersects(LogStat::LOG_CONS)
193 {
194 let syscons =
195 File
196 ::options()
197 .create(false)
198 .read(false)
199 .write(true)
200 .custom_flags(libc::O_NONBLOCK | libc::O_CLOEXEC)
201 .open(*PATH_CONSOLE);
202
203 if let Ok(file) = syscons
204 {
205 let newline = "\n";
206 let _ = send_to_fd(file, msg_payload, newline);
207 }
208 }
209 }
210
211 #[inline]
212 fn is_logmasked(&self, pri: i32) -> bool
213 {
214 return ((1 << (pri & LogMask::LOG_PRIMASK)) & self.logmask) == 0;
215 }
216
217 #[inline]
219 pub(crate)
220 fn get_taptype(&self) -> TapType
221 {
222 return self.stream.get_type();
223 }
224
225 #[inline]
227 pub(crate)
228 fn get_max_msg_size(&self) -> usize
229 {
230 return self.stream.get_max_msg_size();
231 }
232
233 pub(crate)
234 fn set_logmask(&mut self, logmask: i32) -> i32
235 {
236 let oldmask = self.logmask;
237
238 if logmask != 0
239 {
240 self.logmask = logmask;
241 }
242
243 return oldmask;
244 }
245
246 #[inline]
247 pub(crate)
248 fn set_logtag<L: AsRef<str>>(&mut self, logtag: L, update_pid: bool)
249 {
250 self.logtag =
251 truncate_n(logtag.as_ref(), RFC_MAX_APP_NAME).to_string();
252
253 if update_pid == true
254 {
255 self.logpid = portable::get_pid().to_string();
256 }
257
258 return;
259 }
260
261 #[inline]
263 pub(crate)
264 fn disconnectlog(&mut self) -> SyRes<()>
265 {
266 return
267 self
268 .stream
269 .disconnectlog()
270 .map_err(|e| map_error_os!(e, "can not disconnect log properly"));
271 }
272
273 #[inline]
276 pub(crate)
277 fn connectlog(&mut self) -> SyRes<()>
278 {
279 return self.stream.connectlog();
280 }
281
282
283
284 pub(crate)
287 fn vsyslog1(&mut self, mut pri: Priority, fmt: &F)
288 {
289 if let Err(e) = pri.check_invalid_bits()
291 {
292 self.send_to_stderr(&[Cow::Owned(e.to_string())]);
293 }
294
295 if self.is_logmasked(pri.bits()) == true
297 {
298 return;
299 }
300
301 if (pri.bits() & LOG_FACMASK) == 0
303 {
304 pri.set_facility(self.facility);
305 }
306
307 let progname = self.logtag.clone();
308 let pid = self.logpid.clone();
309
310 let msg_formatted =
312 F::vsyslog1_format(self.get_taptype(), self.get_max_msg_size(), pri, &progname, &pid, fmt);
313
314 self.send_to_stderr(msg_formatted.get_stderr_output());
316
317 if self.stream.is_connected() == false
318 {
319 match self.connectlog()
321 {
322 Ok(_) => {},
323 Err(e) =>
324 {
325 self.send_to_stderr(&[Cow::Owned(e.into_inner())]);
326 return;
327 }
328 }
329 }
330
331 let fullmsg = msg_formatted.concat();
332
333 loop
342 {
343 match self.stream.send(fullmsg.as_bytes())
344 {
345 Ok(_) =>
346 return,
347 Err(err) =>
348 {
349 if self.get_taptype().is_network() == false
350 {
351 if let Some(libc::ENOBUFS) = err.raw_os_error()
352 {
353 if self.get_taptype().is_priv() == true
355 {
356 break;
357 }
358
359 sleep(Duration::from_micros(1));
360 }
361 else
362 {
363 let _ = self.disconnectlog();
366 match self.connectlog()
367 {
368 Ok(_) => {},
369 Err(_e) => break,
370 }
371
372 }
374 }
375 else
376 {
377 let _ = self.disconnectlog();
378 match self.connectlog()
379 {
380 Ok(_) => {},
381 Err(e) =>
382 {
383 self.send_to_stderr(&[Cow::Owned(e.to_string())]);
384
385 break;
386 },
387 }
388 }
389 }
390 }
391 } let _ = self.send_to_syscons(msg_formatted.get_stderr_output());
398
399 return;
400 }
401}
402
403
404#[cfg(test)]
405mod tests
406{
407 use super::*;
408
409 #[cfg(target_os = "linux")]
410 #[test]
411 fn test_log_cons()
412 {
413 use crate::{formatters::FormatRfc3146, SyslogLocal};
414
415 let net_tap = SyslogLocal::new();
416
417 let correct =
421 SyncSyslogInternal::<FormatRfc3146, _>::new(Some("test1"), LogStat::LOG_PID | LogStat::LOG_CONS, LogFacility::LOG_DAEMON, net_tap)
422 .unwrap();
423
424 let msg = &[Cow::Borrowed("header msg message payload")];
425
426
427 correct.send_to_syscons(msg);
428
429 return;
430 }
431
432 #[cfg(any(
433 target_os = "freebsd",
434 target_os = "dragonfly",
435 target_os = "openbsd",
436 target_os = "netbsd",
437 target_os = "macos"
438 ))]
439 #[test]
440 fn test_log_cons()
441 {
442 use crate::{formatters::FormatRfc5424, SyslogLocal};
443
444 let net_tap = SyslogLocal::new();
445 let correct =
449 SyncSyslogInternal::<FormatRfc5424, _>::new(Some("test1"), LogStat::LOG_PID | LogStat::LOG_CONS, LogFacility::LOG_DAEMON, net_tap)
450 .unwrap();
451
452 correct.send_to_syscons(&[Cow::Borrowed("header msg"), Cow::Borrowed("message payload")]);
453 }
454
455 #[cfg(target_os = "linux")]
456 #[test]
457 fn test_bit_operations()
458 {
459 use crate::{formatters::FormatRfc3146, SyslogLocal};
460
461 let net_tap = SyslogLocal::new();
462 let correct =
465 SyncSyslogInternal::<FormatRfc3146, _>::new(Some("test1"), LogStat::LOG_PID, LogFacility::LOG_DAEMON, net_tap)
466 .unwrap();
467
468 assert_eq!(correct.facility, LogFacility::LOG_DAEMON);
469 assert_eq!((correct.facility & !LogFacility::LOG_DAEMON), LogFacility::empty());
470 }
471
472 #[cfg(any(
473 target_os = "freebsd",
474 target_os = "dragonfly",
475 target_os = "openbsd",
476 target_os = "netbsd",
477 target_os = "macos"
478 ))]
479 #[test]
480 fn test_bit_operations()
481 {
482 use crate::{formatters::FormatRfc5424, SyslogLocal};
483
484 let net_tap = SyslogLocal::new();
485 let correct =
488 SyncSyslogInternal::<FormatRfc5424, _>::new(Some("test1"), LogStat::LOG_PID, LogFacility::LOG_DAEMON, net_tap)
489 .unwrap();
490
491 assert_eq!(correct.facility, LogFacility::LOG_DAEMON);
492 assert_eq!((correct.facility & !LogFacility::LOG_DAEMON), LogFacility::empty());
493 }
494
495 #[cfg(target_os = "linux")]
496 #[test]
497 fn test_bit_operations2()
498 {
499 use crate::{formatters::FormatRfc3146, SyslogLocal};
500
501 let net_tap = SyslogLocal::new();
502 let _correct =
505 SyncSyslogInternal::<FormatRfc3146, _>::new(Some("test2"), LogStat::LOG_PID, LogFacility::LOG_DAEMON, net_tap)
506 .unwrap();
507
508 let mut pri = Priority::LOG_ALERT;
509
510 let res = pri.check_invalid_bits();
511
512 assert_eq!(res.is_ok(), true);
513 assert_eq!(pri.bits(), Priority::LOG_ALERT.bits());
514 }
515
516 #[cfg(any(
517 target_os = "freebsd",
518 target_os = "dragonfly",
519 target_os = "openbsd",
520 target_os = "netbsd",
521 target_os = "macos"
522 ))]
523 #[test]
524 fn test_bit_operations2()
525 {
526 use crate::{formatters::FormatRfc5424, SyslogLocal};
527
528 let net_tap = SyslogLocal::new();
529 let _correct =
532 SyncSyslogInternal::<FormatRfc5424, _>::new(Some("test2"), LogStat::LOG_PID, LogFacility::LOG_DAEMON, net_tap)
533 .unwrap();
534
535 let mut pri = Priority::LOG_ALERT;
536
537 let res = pri.check_invalid_bits();
538
539 assert_eq!(res.is_ok(), true);
540 assert_eq!(pri.bits(), Priority::LOG_ALERT.bits());
541 }
542
543 #[cfg(target_os = "linux")]
544 #[test]
545 fn test_set_priority()
546 {
547 use crate::{ formatters::FormatRfc3146, SyslogLocal, LOG_MASK};
548
549 let net_tap = SyslogLocal::new();
550 let mut correct =
553 SyncSyslogInternal::<FormatRfc3146, _>::new(Some("test1"), LogStat::LOG_PID, LogFacility::LOG_DAEMON, net_tap)
554 .unwrap();
555
556 let ret = correct.set_logmask(LOG_MASK!(Priority::LOG_ERR));
557
558 assert_eq!(ret, 0xff);
559
560 let ret = correct.set_logmask(LOG_MASK!(Priority::LOG_ERR));
561
562 assert_eq!(ret, LOG_MASK!(Priority::LOG_ERR));
563
564 let ret = correct.is_logmasked(Priority::LOG_WARNING.bits());
565 assert_eq!(ret, true);
566
567 let ret = correct.is_logmasked(Priority::LOG_ERR.bits());
568 assert_eq!(ret, false);
569
570 let ret = correct.is_logmasked(Priority::LOG_CRIT.bits());
571 assert_eq!(ret, true);
572 }
573
574 #[cfg(any(
575 target_os = "freebsd",
576 target_os = "dragonfly",
577 target_os = "openbsd",
578 target_os = "netbsd",
579 target_os = "macos"
580 ))]
581 #[test]
582 fn test_set_priority()
583 {
584 use crate::{ formatters::FormatRfc5424, SyslogLocal, LOG_MASK};
585
586 let net_tap = SyslogLocal::new();
587 let mut correct =
590 SyncSyslogInternal::<FormatRfc5424, _>::new(Some("test1"), LogStat::LOG_PID, LogFacility::LOG_DAEMON, net_tap)
591 .unwrap();
592
593 let ret = correct.set_logmask(LOG_MASK!(Priority::LOG_ERR));
594
595 assert_eq!(ret, 0xff);
596
597 let ret = correct.set_logmask(LOG_MASK!(Priority::LOG_ERR));
598
599 assert_eq!(ret, LOG_MASK!(Priority::LOG_ERR));
600
601 let ret = correct.is_logmasked(Priority::LOG_WARNING.bits());
602 assert_eq!(ret, true);
603
604 let ret = correct.is_logmasked(Priority::LOG_ERR.bits());
605 assert_eq!(ret, false);
606
607 let ret = correct.is_logmasked(Priority::LOG_CRIT.bits());
608 assert_eq!(ret, true);
609 }
610
611 #[cfg(target_os = "linux")]
612 #[test]
613 fn test_set_priority2()
614 {
615 use crate::{ formatters::FormatRfc3146, SyslogLocal, LOG_MASK};
616
617 let net_tap = SyslogLocal::new();
618 let mut correct =
621 SyncSyslogInternal::<FormatRfc3146, _>::new(Some("test1"), LogStat::LOG_PID, LogFacility::LOG_DAEMON, net_tap)
622 .unwrap();
623
624 let ret = correct.set_logmask(!LOG_MASK!(Priority::LOG_ERR));
625
626 assert_eq!(ret, 0xff);
627
628 let ret = correct.is_logmasked(Priority::LOG_WARNING.bits());
629 assert_eq!(ret, false);
630
631 let ret = correct.is_logmasked(Priority::LOG_ERR.bits());
632 assert_eq!(ret, true);
633
634 let ret = correct.is_logmasked(Priority::LOG_CRIT.bits());
635 assert_eq!(ret, false);
636 }
637
638 #[cfg(any(
639 target_os = "freebsd",
640 target_os = "dragonfly",
641 target_os = "openbsd",
642 target_os = "netbsd",
643 target_os = "macos"
644 ))]
645 #[test]
646 fn test_set_priority2()
647 {
648 use crate::{ formatters::FormatRfc5424, SyslogLocal, LOG_MASK};
649
650 let net_tap = SyslogLocal::new();
651 let mut correct =
654 SyncSyslogInternal::<FormatRfc5424, _>::new(Some("test1"), LogStat::LOG_PID, LogFacility::LOG_DAEMON, net_tap)
655 .unwrap();
656
657 let ret = correct.set_logmask(!LOG_MASK!(Priority::LOG_ERR));
658
659 assert_eq!(ret, 0xff);
660
661 let ret = correct.is_logmasked(Priority::LOG_WARNING.bits());
662 assert_eq!(ret, false);
663
664 let ret = correct.is_logmasked(Priority::LOG_ERR.bits());
665 assert_eq!(ret, true);
666
667 let ret = correct.is_logmasked(Priority::LOG_CRIT.bits());
668 assert_eq!(ret, false);
669 }
670}