Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 32 additions & 34 deletions src/syslog.rs
Original file line number Diff line number Diff line change
Expand Up @@ -289,15 +289,13 @@ impl<'a> MakeWriter<'a> for Syslog {

/// [Writer](io::Write) to `syslog` produced by [`MakeWriter`].
pub struct SyslogWriter {
flushed: bool,
facility: Facility,
level: Level,
}

impl SyslogWriter {
fn new(facility: Facility, level: Level) -> Self {
SyslogWriter {
flushed: false,
facility,
level,
}
Expand All @@ -308,45 +306,45 @@ thread_local! { static BUF: RefCell<Vec<u8>> = RefCell::new(Vec::with_capacity(2

impl io::Write for SyslogWriter {
fn write(&mut self, bytes: &[u8]) -> io::Result<usize> {
BUF.with(|buf| buf.borrow_mut().extend(bytes));
Ok(bytes.len())
}
// Check if the data is already a cstr
if let Ok(cstr) = CStr::from_bytes_with_nul(bytes) {
syslog(Priority::new(self.facility, self.level), cstr);
return Ok(bytes.len());
}

fn flush(&mut self) -> io::Result<()> {
// If we got here, it means that there's an interior nul or it's not nul
// terminated

if bytes.last() == Some(&0x00) {
// We're nul terminated, which means that we must have had a
// interior nul.
//
// Interior nuls are never valid CStrs, so instead of truncating,
// fail instead.
return Err(io::Error::new(io::ErrorKind::InvalidData, "Data provided to syslog must be not have interior nuls"));
}

// We have a non-nul terminated string; Re-use the buffer to create a
// nul-terminated cstr
#[cfg(debug_assertions)]
assert!(bytes.iter().all(|b| *b != 0x00), "we should have non-null data here");
BUF.with(|buf| {
let mut buf = buf.borrow_mut();

// Append nul-terminator
buf.push(0);

// Send the message to `syslog` if the message is valid
match CStr::from_bytes_with_nul(&buf) {
Ok(msg) => syslog(Priority::new(self.facility, self.level), msg),
Err::<_, std::ffi::FromBytesWithNulError>(err) => {
// Since we push a nul byte to `buf` above, it must be that `buf` contained an
// interior nul byte. In debug mode, panic
#[cfg(debug_assertions)]
panic!("message to be logged contained interior nul byte: {}", err);
// ... but in non-debug mode, just print an error
#[cfg(not(debug_assertions))]
eprintln!("message to be logged contained interior nul byte: {}", err);
}
}

// Clear buffer
buf.clear();

self.flushed = true;
Ok(())
buf.extend_from_slice(bytes);
buf.push(0x00);

// SAFETY: We just added a nul terminator and asserted that the
// data has no interior nuls. We also cleared the buffer, so the
// only data in there is a interior-nul-free, nul-terminated slice.
let cstr = unsafe { CStr::from_bytes_with_nul_unchecked(&buf) };
syslog(Priority::new(self.facility, self.level), cstr);
Ok(bytes.len())
})
}
}

impl Drop for SyslogWriter {
fn drop(&mut self) {
if !self.flushed {
let _ = io::Write::flush(self);
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}

Expand Down