syslog_rs/sync/
syslog_sync_internal.rs1use std::io::ErrorKind;
18use std::thread::sleep;
19use std::time::Duration;
20
21
22use instance_copy_on_write::ICoW;
23use nix::libc;
24
25
26use crate::error::SyslogErrCode;
27use crate::formatters::SyslogFormatted;
28use crate::formatters::SyslogFormatter;
29use crate::map_error_code;
30use crate::portable;
31use crate::common::*;
32use crate::error::{SyRes};
33use crate::SyslogDestination;
34
35use super::socket::*;
36
37
38#[derive(Debug, Clone)]
39pub(crate) struct LogItems
40{
41 pub(crate) logtag: String,
43
44 pub(crate) logpid: String,
46
47 pub(crate) logmask: i32,
49
50 pub(crate) logstat: LogStat,
51
52 pub(crate) facility: LogFacility,
54}
55
56impl LogItems
57{
58 #[inline]
59 pub
60 fn new(ident: Option<&str>, logmask: i32, logstat: LogStat, facility: LogFacility) -> Self
61 {
62 let mut log_inst =
63 Self
64 {
65 logtag: String::new(),
66 logpid: portable::get_pid().to_string(),
67 logmask: logmask,
68 logstat: logstat,
69 facility: LogFacility::empty(),
70 };
71
72 log_inst.set_log_facility(facility);
73 log_inst.set_identity(ident);
74
75 return log_inst;
76 }
77
78 pub(crate)
79 fn set_log_facility(&mut self, facility: LogFacility)
80 {
81 let log_facility =
82 if facility.is_empty() == false &&
83 (facility & !LogMask::LOG_FACMASK).is_empty() == true
84 {
85 facility
86 }
87 else
88 {
89 LogFacility::LOG_USER
91 };
92
93 self.facility = log_facility;
94
95 return;
96 }
97
98 pub(crate)
99 fn set_identity(&mut self, ident: Option<&str>)
100 {
101 let logtag =
102 match ident
103 {
104 Some(r) =>
105 truncate_n(r, RFC_MAX_APP_NAME).to_string(),
106 None =>
107 truncate_n(
108 portable::p_getprogname()
109 .unwrap_or("notavail".to_string())
110 .as_str(),
111 RFC_MAX_APP_NAME
112 )
113 .to_string()
114 };
115
116 self.logtag = logtag;
117
118 return;
119 }
120
121 #[inline]
122 pub
123 fn get_progname(&self) -> &str
124 {
125 return &self.logtag;
126 }
127
128 #[inline]
129 pub
130 fn get_pid(&self) -> &str
131 {
132 return &self.logtag;
133 }
134
135 #[inline]
136 fn is_logmasked(&self, pri: i32) -> bool
137 {
138 return ((1 << (pri & LogMask::LOG_PRIMASK)) & self.logmask) == 0;
139 }
140
141 pub(crate)
142 fn set_logmask(&mut self, logmask: i32) -> i32
143 {
144 let oldmask = self.logmask;
145
146 if logmask != 0
147 {
148 self.logmask = logmask;
149 }
150
151 return oldmask;
152 }
153
154 pub(crate)
155 fn vsyslog1_msg<F, D>(&self, mut pri: Priority, fmt: &F) -> Option<(SyslogFormatted, LogStat)>
156 where F: SyslogFormatter, D: SyslogDestination
157 {
158 if let Err(e) = pri.check_invalid_bits()
160 {
161 self.logstat.send_to_stderr(&e.to_string());
162 }
163
164 if self.is_logmasked(pri.bits()) == true
166 {
167 return None;
168 }
169
170 if (pri.bits() & LOG_FACMASK) == 0
172 {
173 pri.set_facility(self.facility);
174 }
175
176 let msg_formatted =
178 fmt.vsyslog1_format(D::SocketTap::get_max_msg_size(), pri, &self.logtag, &self.logpid);
179
180 self.logstat.send_to_stderr(msg_formatted.get_stderr_output());
182
183 return Some((msg_formatted, self.logstat));
184 }
185}
186
187#[derive(Debug)]
188pub(crate) struct SyslogSocket<D: SyslogDestination>
189{
190 stream: ICoW<D::SocketTap>
191}
192
193impl<D: SyslogDestination> SyslogSocket<D>
194{
195 pub(crate)
196 fn new(logstat: LogStat, net_tap_prov: D) -> SyRes<Self>
197 {
198 let mut sock =
199 D::SocketTap::new(net_tap_prov)?;
200
201 if logstat.contains(LogStat::LOG_NDELAY) == true
202 {
203 sock.connectlog()?;
204 }
205
206 return Ok(
207 Self{ stream: ICoW::new(sock) }
208 );
209 }
210
211 #[inline]
212 pub(crate)
213 fn update_tap_data(&self, tap_data: D) -> SyRes<()>
214 {
215
216 let mut lock =
217 self
218 .stream
219 .try_clone_exclusivly()
220 .ok_or_else(||
221 map_error_code!(CoWAlreadyLocked, "cannot exclusivly lock instance, already locked")
222 )?;
223
224 lock.update_tap_data(tap_data);
227
228 lock.connectlog()?;
229
230 lock.commit();
231
232 return Ok(());
233 }
234
235 #[inline]
236 pub(crate)
237 fn reconnectlog(&self) -> SyRes<()>
238 {
239 let mut lock =
240 self
241 .stream
242 .try_clone_exclusivly()
243 .ok_or_else(||
244 map_error_code!(CoWAlreadyLocked, "cannot exclusivly lock instance, already locked")
245 )?;
246
247 lock.connectlog()?;
249
250 lock.commit();
251
252 return Ok(());
253 }
254
255 #[inline]
256 pub(crate)
257 fn connectlog(&self) -> SyRes<()>
258 {
259 let mut lock =
260 self
261 .stream
262 .try_clone_exclusivly()
263 .ok_or_else(||
264 map_error_code!(CoWAlreadyLocked, "cannot exclusivly lock instance, already locked")
265 )?;
266 lock.connectlog()?;
272
273 lock.commit();
274
275 return Ok(());
276 }
277
278 #[inline]
281 pub(crate)
282 fn disconnectlog(&self) -> SyRes<()>
283 {
284 let lock =
285 self
286 .stream
287 .try_clone_exclusivly()
288 .ok_or_else(||
289 map_error_code!(CoWAlreadyLocked, "cannot exclusivly lock instance, already locked")
290 )?;
291
292 lock.commit();
298
299 return Ok(());
300 }
301
302 pub(crate)
311 fn vsyslog1(&self, logstat: LogStat, mut msg_formatted: SyslogFormatted)
312 {
313 let fullmsg = msg_formatted.get_full_msg();
314
315 let mut repeated = false;
316 loop
317 {
318 let (tap_type, res) =
319 {
320 let read0 = self.stream.read();
321
322 (read0.get_type(), read0.send(fullmsg.as_bytes()))
323 };
324
325 match res
326 {
327 Ok(_) =>
328 break,
329 Err(ref e) if e.kind() == ErrorKind::NotConnected =>
330 {
331 if let Err(e) = self.connectlog()
332 {
333 if e.get_errcode() == SyslogErrCode::CoWAlreadyLocked
334 {
335 continue;
336 }
337 else
338 {
339 logstat.send_to_stderr(&format!("connectlog() failed with {}", e));
340 break;
341 }
342 }
343 },
344 Err(err) =>
345 {
346 if D::DEST_TYPE.is_network() == false
347 {
348 if let Some(libc::ENOBUFS) = err.raw_os_error()
349 {
350 if tap_type.is_priv() == true
352 {
353 break;
354 }
355
356 sleep(Duration::from_micros(1));
357 }
358 else
359 {
360 if let Err(e) = self.reconnectlog()
362 {
363 if e.get_errcode() == SyslogErrCode::CoWAlreadyLocked
364 {
365 continue;
366 }
367 else
368 {
369 logstat.send_to_stderr(&format!("reconnectlog() failed with {}", e));
370 break;
371 }
372 }
373
374 }
376 }
377 else
378 {
379 if let Err(e) = self.connectlog()
380 {
381 if e.get_errcode() == SyslogErrCode::CoWAlreadyLocked
382 {
383 continue;
384 }
385 else
386 {
387 logstat.send_to_stderr(&format!("connectlog() failed with {}", e));
388 break;
389 }
390 }
391 }
392
393 }
394 }
395 }
396
397
398 let _ = logstat.send_to_syscons(msg_formatted.get_stderr_output());
402
403 return;
404 }
405
406}
407
408#[cfg(any(feature = "build_with_thread_local", feature = "build_with_queue"))]
409pub mod lockless
410{
411 use super::*;
412
413 #[derive(Debug)]
414 pub(crate) struct SyslogSocketLockless<D: SyslogDestination>
415 {
416 stream: D::SocketTap
417 }
418
419 impl<D: SyslogDestination> SyslogSocketLockless<D>
420 {
421 pub(crate)
422 fn new(logstat: LogStat, net_tap_prov: D) -> SyRes<Self>
423 {
424 let mut sock =
425 D::SocketTap::new(net_tap_prov)?;
426
427 if logstat.contains(LogStat::LOG_NDELAY) == true
428 {
429 sock.connectlog()?;
430 }
431
432 return Ok(
433 Self{ stream: sock }
434 );
435 }
436
437 #[inline]
438 pub(crate)
439 fn update_tap_data(&mut self, tap_data: D) -> SyRes<()>
440 {
441 self.stream.disconnectlog()?;
442 self.stream.update_tap_data(tap_data);
443
444 return self.stream.connectlog();
445 }
446
447 #[inline]
448 pub(crate)
449 fn reconnectlog(&mut self) -> SyRes<()>
450 {
451 self.stream.disconnectlog()?;
452 self.stream.connectlog()?;
453
454 return Ok(());
455 }
456
457 #[inline]
458 pub(crate)
459 fn connectlog(&mut self) -> SyRes<()>
460 {
461 return self.stream.connectlog();
462 }
463
464 #[inline]
467 pub(crate)
468 fn disconnectlog(&mut self) -> SyRes<()>
469 {
470 return self.stream.disconnectlog();
471 }
472
473 pub(crate)
482 fn vsyslog1(&mut self, logstat: LogStat, mut msg_formatted: SyslogFormatted)
483 {
484 if self.stream.is_connected() == false
485 {
486 if let Err(e) = self.stream.connectlog()
487 {
488 logstat.send_to_stderr(&e.into_inner());
489 }
490 }
491
492 let fullmsg = msg_formatted.get_full_msg();
493
494 let mut repeated = false;
495 loop
496 {
497 match self.stream.send(fullmsg.as_bytes())
498 {
499 Ok(_) =>
500 break,
501 Err(ref e) if e.kind() == ErrorKind::NotConnected =>
502 {
503 break;
504 },
505 Err(ref e) if e.kind() == ErrorKind::Deadlock =>
507 {
508 logstat.send_to_stderr(&e.to_string());
509 return;
510 },
511 Err(err) =>
512 {
513 if D::DEST_TYPE.is_network() == false
514 {
515 if let Some(libc::ENOBUFS) = err.raw_os_error()
516 {
517 if self.stream.get_type().is_priv() == true
519 {
520 break;
521 }
522
523 sleep(Duration::from_micros(1));
524 }
525 else
526 {
527 let _ = self.stream.disconnectlog();
529 if let Err(e) = self.stream.connectlog()
530 {
531 logstat.send_to_stderr(&e.into_inner());
532 break;
533 }
534 }
535
536 }
538 else
539 {
540 if repeated == false
541 {
542 repeated = true;
543
544 let _ = self.stream.disconnectlog();
545 if let Err(e) = self.stream.connectlog()
546 {
547 logstat.send_to_stderr(&e.into_inner());
548 break;
549 }
550 }
551 else
552 {
553 logstat.send_to_stderr("syslog: can not send to remote server");
554 break;
555 }
556 }
557
558 }
559 }
560 }
561
562
563 let _ = logstat.send_to_syscons(msg_formatted.get_stderr_output());
567
568 return;
569 }
570
571 }
572}
573
574#[cfg(any(feature = "build_with_thread_local", feature = "build_with_queue"))]
575pub use self::lockless::*;
576
577
578
579#[cfg(test)]
580mod tests
581{
582 use crate::LOG_MASK;
583
584 use super::*;
585
586 #[test]
587 fn test_log_cons()
588 {
589
590
591 let msg = "header msg message payload";
594
595 let lsts = LogStat::LOG_CONS;
596
597 lsts.send_to_syscons(msg);
598
599 return;
600 }
601
602 #[test]
603 fn test_bit_operations()
604 {
605
606 let correct =
607 LogItems::new(Some("test1"), 0xFF, LogStat::LOG_PID, LogFacility::LOG_DAEMON);
608
609 assert_eq!(correct.facility, LogFacility::LOG_DAEMON);
610 assert_eq!((correct.facility & !LogFacility::LOG_DAEMON), LogFacility::empty());
611 }
612
613 #[test]
614 fn test_bit_operations2()
615 {
616
617 let mut pri = Priority::LOG_ALERT;
618
619 let res = pri.check_invalid_bits();
620
621 assert_eq!(res.is_ok(), true);
622 assert_eq!(pri.bits(), Priority::LOG_ALERT.bits());
623 }
624
625 #[test]
626 fn test_set_priority()
627 {
628 let mut correct =
629 LogItems::new(Some("test1"), 0xFF, LogStat::LOG_PID, LogFacility::LOG_DAEMON);
630
631 let ret = correct.set_logmask(LOG_MASK!(Priority::LOG_ERR));
632
633 assert_eq!(ret, 0xff);
634
635 let ret = correct.set_logmask(LOG_MASK!(Priority::LOG_ERR));
636
637 assert_eq!(ret, LOG_MASK!(Priority::LOG_ERR));
638
639 let ret = correct.is_logmasked(Priority::LOG_WARNING.bits());
640 assert_eq!(ret, true);
641
642 let ret = correct.is_logmasked(Priority::LOG_ERR.bits());
643 assert_eq!(ret, false);
644
645 let ret = correct.is_logmasked(Priority::LOG_CRIT.bits());
646 assert_eq!(ret, true);
647 }
648}
649