1use std::borrow::Cow;
24use std::marker::PhantomData;
25use std::thread::sleep;
26use std::time::Duration;
27
28use nix::libc;
29
30#[cfg(any(
31 target_os = "freebsd",
32 target_os = "dragonfly",
33 target_os = "openbsd",
34 target_os = "netbsd",
35 target_os = "macos"
36))]
37use chrono::SecondsFormat;
38
39use crate::formatters::SyslogFormatter;
40use crate::map_error_os;
41use crate::portable;
42use crate::common::*;
43use crate::error::{SyRes};
44use crate::socket::TapType;
45use crate::socket::TapTypeData;
46
47use super::socket::*;
48
49#[derive(Debug)]
51pub struct SyncSyslogInternal<F: SyslogFormatter>
52{
53 logtag: String,
55
56 logpid: String,
58
59 logstat: LogStat,
61
62 facility: LogFacility,
64
65 logmask: i32,
67
68 stream: Box<dyn SyslogTap + Send>,
70
71 _p: PhantomData<F>,
72}
73
74
75impl<F: SyslogFormatter> Drop for SyncSyslogInternal<F>
76{
77 fn drop(&mut self)
78 {
79 let _ = self.disconnectlog();
81 }
82}
83
84impl<F: SyslogFormatter> SyncSyslogInternal<F>
87{
88 pub(crate)
111 fn new(
112 ident: Option<&str>,
113 logstat: LogStat,
114 facility: LogFacility,
115 req_tap: TapTypeData
116 ) -> SyRes<Self>
117 {
118 let log_facility =
120 if facility.is_empty() == false &&
121 (facility & !LogMask::LOG_FACMASK).is_empty() == true
122 {
123 facility
124 }
125 else
126 {
127 LogFacility::LOG_USER
129 };
130
131 let logtag =
132 match ident
133 {
134 Some(r) =>
135 truncate_n(r, RFC_MAX_APP_NAME).to_string(),
136 None =>
137 truncate_n(
138 portable::p_getprogname()
139 .unwrap_or("".to_string())
140 .as_str(),
141 RFC_MAX_APP_NAME
142 )
143 .to_string()
144 };
145
146 return Ok(
147 Self
148 {
149 logtag: logtag,
150 logpid: portable::get_pid().to_string(),
151 logstat: logstat,
152 facility: log_facility,
153 logmask: 0xff,
154 stream: Tap::<()>::new(req_tap)?,
155 _p: PhantomData
156 }
157 );
158 }
159
160 pub(crate)
162 fn update_tap_data(&mut self, tap_data: TapTypeData) -> SyRes<()>
163 {
164 let is_con = self.stream.is_connected();
165
166 if is_con == true
167 {
168 self
169 .stream
170 .disconnectlog()
171 .map_err(|e|
172 map_error_os!(e, "update_tap_data() can not disconnect log properly")
173 )?;
174 }
175
176 self.stream.update_tap_data(tap_data)?;
177
178 if is_con == true
179 {
180 self.stream.connectlog()?;
182 }
183
184 return Ok(());
185 }
186
187 #[inline]
188 pub(super)
189 fn send_to_stderr(&self, msg: &[Cow<'_, str>])
190 {
191 if self.logstat.intersects(LogStat::LOG_PERROR) == true
192 {
193 let stderr_lock = std::io::stderr().lock();
194 let newline = "\n";
195
196 let _ = send_to_fd(stderr_lock, msg, &newline);
197 }
198 }
199
200 #[inline]
201 fn send_to_syscons(&self, msg_payload: &[Cow<'_, str>])
202 {
203 use std::fs::File;
204 use std::os::unix::fs::OpenOptionsExt;
205
206 if self.logstat.intersects(LogStat::LOG_CONS)
207 {
208 let syscons =
209 File
210 ::options()
211 .create(false)
212 .read(false)
213 .write(true)
214 .custom_flags(libc::O_NONBLOCK | libc::O_CLOEXEC)
215 .open(*PATH_CONSOLE);
216
217 if let Ok(file) = syscons
218 {
219 let newline = "\n";
220 let _ = send_to_fd(file, msg_payload, newline);
221 }
222 }
223 }
224
225 #[inline]
226 fn is_logmasked(&self, pri: i32) -> bool
227 {
228 return ((1 << (pri & LogMask::LOG_PRIMASK)) & self.logmask) == 0;
229 }
230
231 #[inline]
233 pub(crate)
234 fn get_tap_type(&self) -> TapType
235 {
236 return self.stream.get_type();
237 }
238
239 pub(crate)
240 fn set_logmask(&mut self, logmask: i32) -> i32
241 {
242 let oldmask = self.logmask;
243
244 if logmask != 0
245 {
246 self.logmask = logmask;
247 }
248
249 return oldmask;
250 }
251
252 #[inline]
253 pub(crate)
254 fn set_logtag<L: AsRef<str>>(&mut self, logtag: L, update_pid: bool)
255 {
256 self.logtag =
257 truncate_n(logtag.as_ref(), RFC_MAX_APP_NAME).to_string();
258
259 if update_pid == true
260 {
261 self.logpid = portable::get_pid().to_string();
262 }
263
264 return;
265 }
266
267 #[inline]
269 pub(crate)
270 fn disconnectlog(&mut self) -> SyRes<()>
271 {
272 return
273 self
274 .stream
275 .disconnectlog()
276 .map_err(|e| map_error_os!(e, "can not disconnect log properly"));
277 }
278
279 #[inline]
282 pub(crate)
283 fn connectlog(&mut self) -> SyRes<()>
284 {
285 return self.stream.connectlog();
286 }
287
288 pub(crate)
291 fn vsyslog1(&mut self, mut pri: Priority, fmt: &str)
292 {
293 if let Err(e) = pri.check_invalid_bits()
295 {
296 self.send_to_stderr(&[Cow::Owned(e.to_string())]);
297 }
298
299 if self.is_logmasked(pri.bits()) == true
307 {
308 return;
309 }
310
311 if (pri.bits() & LOG_FACMASK) == 0
313 {
314 pri.set_facility(self.facility);
315 }
316
317 let progname = self.logtag.clone();
318 let pid = self.logpid.clone();
319
320 let msg_formatted = F::vsyslog1_format(self.get_tap_type(), pri, &progname, &pid, fmt);
321
322 self.send_to_stderr(msg_formatted.get_stderr_output());
324
325 if self.stream.is_connected() == false
326 {
327 match self.connectlog()
329 {
330 Ok(_) => {},
331 Err(e) =>
332 {
333 self.send_to_stderr(&[Cow::Owned(e.into_inner())]);
334 return;
335 }
336 }
337 }
338
339 let fullmsg = msg_formatted.concat();
340
341 loop
350 {
351 match self.stream.send(fullmsg.as_bytes())
352 {
353 Ok(_) => return,
354 Err(err) =>
355 {
356 if self.get_tap_type().is_network() == false
357 {
358 if let Some(libc::ENOBUFS) = err.raw_os_error()
359 {
360 if self.get_tap_type().is_priv() == true
362 {
363 break;
364 }
365
366 sleep(Duration::from_micros(1));
367 }
368 else
369 {
370 let _ = self.disconnectlog();
373 match self.connectlog()
374 {
375 Ok(_) => {},
376 Err(_e) => break,
377 }
378
379 }
381 }
382 else
383 {
384 let _ = self.disconnectlog();
385 match self.connectlog()
386 {
387 Ok(_) => {},
388 Err(_e) => break,
389 }
390 }
391 }
392 }
393 } let _ = self.send_to_syscons(msg_formatted.get_stderr_output());
400
401 return;
402 }
403}
404
405
406#[cfg(test)]
407mod tests
408{
409 use super::*;
410
411 #[cfg(target_os = "linux")]
412 #[test]
413 fn test_log_cons()
414 {
415 use crate::formatters::FormatRfc3146;
416
417
418 let net_tap = TapTypeData::new_unix(None, true).unwrap();
419
420 let correct =
422 SyncSyslogInternal::<FormatRfc3146>::new(Some("test1"), LogStat::LOG_PID | LogStat::LOG_CONS, LogFacility::LOG_DAEMON, net_tap)
423 .unwrap();
424
425 let msg = &[Cow::Borrowed("header msg message payload")];
426
427
428 correct.send_to_syscons(msg);
429
430 return;
431 }
432
433 #[cfg(any(
434 target_os = "freebsd",
435 target_os = "dragonfly",
436 target_os = "openbsd",
437 target_os = "netbsd",
438 target_os = "macos"
439 ))]
440 #[test]
441 fn test_log_cons()
442 {
443 use crate::formatters::FormatRfc5424;
444
445
446 let net_tap = TapTypeData::new_unix(None, true).unwrap();
447
448 let correct =
450 SyncSyslogInternal::<FormatRfc5424>::new(Some("test1"), LogStat::LOG_PID | LogStat::LOG_CONS, LogFacility::LOG_DAEMON, net_tap)
451 .unwrap();
452
453 correct.send_to_syscons(&[Cow::Borrowed("header msg"), Cow::Borrowed("message payload")]);
454 }
455
456 #[cfg(target_os = "linux")]
457 #[test]
458 fn test_bit_operations()
459 {
460 use crate::formatters::FormatRfc3146;
461
462 let net_tap = TapTypeData::new_unix(None, true).unwrap();
463
464 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;
483
484
485 let net_tap = TapTypeData::new_unix(None, true).unwrap();
486
487 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;
500
501 let net_tap = TapTypeData::new_unix(None, true).unwrap();
502
503 let _correct =
504 SyncSyslogInternal::<FormatRfc3146>::new(Some("test2"), LogStat::LOG_PID, LogFacility::LOG_DAEMON, net_tap)
505 .unwrap();
506
507 let mut pri = Priority::LOG_ALERT;
508
509 let res = pri.check_invalid_bits();
510
511 assert_eq!(res.is_ok(), true);
512 assert_eq!(pri.bits(), Priority::LOG_ALERT.bits());
513 }
514
515 #[cfg(any(
516 target_os = "freebsd",
517 target_os = "dragonfly",
518 target_os = "openbsd",
519 target_os = "netbsd",
520 target_os = "macos"
521 ))]
522 #[test]
523 fn test_bit_operations2()
524 {
525 use crate::formatters::FormatRfc5424;
526
527 let net_tap = TapTypeData::new_unix(None, true).unwrap();
528
529 let _correct =
530 SyncSyslogInternal::<FormatRfc5424>::new(Some("test2"), LogStat::LOG_PID, LogFacility::LOG_DAEMON, net_tap)
531 .unwrap();
532
533 let mut pri = Priority::LOG_ALERT.bits();
534
535 let res = check_invalid_bits(&mut pri);
536
537 assert_eq!(res.is_ok(), true);
538 assert_eq!(pri, Priority::LOG_ALERT.bits());
539 }
540
541 #[cfg(target_os = "linux")]
542 #[test]
543 fn test_set_priority()
544 {
545 use crate::{ formatters::FormatRfc3146, LOG_MASK};
546
547 let net_tap = TapTypeData::new_unix(None, true).unwrap();
548
549 let mut correct =
550 SyncSyslogInternal::<FormatRfc3146>::new(Some("test1"), LogStat::LOG_PID, LogFacility::LOG_DAEMON, net_tap)
551 .unwrap();
552
553 let ret = correct.set_logmask(LOG_MASK!(Priority::LOG_ERR));
554
555 assert_eq!(ret, 0xff);
556
557 let ret = correct.set_logmask(LOG_MASK!(Priority::LOG_ERR));
558
559 assert_eq!(ret, LOG_MASK!(Priority::LOG_ERR));
560
561 let ret = correct.is_logmasked(Priority::LOG_WARNING.bits());
562 assert_eq!(ret, true);
563
564 let ret = correct.is_logmasked(Priority::LOG_ERR.bits());
565 assert_eq!(ret, false);
566
567 let ret = correct.is_logmasked(Priority::LOG_CRIT.bits());
568 assert_eq!(ret, true);
569 }
570
571 #[cfg(any(
572 target_os = "freebsd",
573 target_os = "dragonfly",
574 target_os = "openbsd",
575 target_os = "netbsd",
576 target_os = "macos"
577 ))]
578 #[test]
579 fn test_set_priority()
580 {
581 use crate::{ formatters::FormatRfc5424, LOG_MASK};
582
583 let net_tap = TapTypeData::new_unix(None, true).unwrap();
584
585 let mut correct =
586 SyncSyslogInternal::<FormatRfc5424>::new(Some("test1"), LogStat::LOG_PID, LogFacility::LOG_DAEMON, net_tap)
587 .unwrap();
588
589 let ret = correct.set_logmask(LOG_MASK!(Priority::LOG_ERR));
590
591 assert_eq!(ret, 0xff);
592
593 let ret = correct.set_logmask(LOG_MASK!(Priority::LOG_ERR));
594
595 assert_eq!(ret, LOG_MASK!(Priority::LOG_ERR));
596
597 let ret = correct.is_logmasked(Priority::LOG_WARNING.bits());
598 assert_eq!(ret, true);
599
600 let ret = correct.is_logmasked(Priority::LOG_ERR.bits());
601 assert_eq!(ret, false);
602
603 let ret = correct.is_logmasked(Priority::LOG_CRIT.bits());
604 assert_eq!(ret, true);
605 }
606
607 #[cfg(target_os = "linux")]
608 #[test]
609 fn test_set_priority2()
610 {
611 use crate::{ formatters::FormatRfc3146, LOG_MASK};
612
613 let net_tap = TapTypeData::new_unix(None, true).unwrap();
614
615 let mut correct =
616 SyncSyslogInternal::<FormatRfc3146>::new(Some("test1"), LogStat::LOG_PID, LogFacility::LOG_DAEMON, net_tap)
617 .unwrap();
618
619 let ret = correct.set_logmask(!LOG_MASK!(Priority::LOG_ERR));
620
621 assert_eq!(ret, 0xff);
622
623 let ret = correct.is_logmasked(Priority::LOG_WARNING.bits());
624 assert_eq!(ret, false);
625
626 let ret = correct.is_logmasked(Priority::LOG_ERR.bits());
627 assert_eq!(ret, true);
628
629 let ret = correct.is_logmasked(Priority::LOG_CRIT.bits());
630 assert_eq!(ret, false);
631 }
632
633 #[cfg(any(
634 target_os = "freebsd",
635 target_os = "dragonfly",
636 target_os = "openbsd",
637 target_os = "netbsd",
638 target_os = "macos"
639 ))]
640 #[test]
641 fn test_set_priority2()
642 {
643 use crate::{ formatters::FormatRfc5424, LOG_MASK};
644
645 let net_tap = TapTypeData::new_unix(None, true).unwrap();
646
647 let mut correct =
648 SyncSyslogInternal::<FormatRfc5424>::new(Some("test1"), LogStat::LOG_PID, LogFacility::LOG_DAEMON, net_tap)
649 .unwrap();
650
651 let ret = correct.set_logmask(!LOG_MASK!(Priority::LOG_ERR));
652
653 assert_eq!(ret, 0xff);
654
655 let ret = correct.is_logmasked(Priority::LOG_WARNING.bits());
656 assert_eq!(ret, false);
657
658 let ret = correct.is_logmasked(Priority::LOG_ERR.bits());
659 assert_eq!(ret, true);
660
661 let ret = correct.is_logmasked(Priority::LOG_CRIT.bits());
662 assert_eq!(ret, false);
663 }
664}