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