1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168
// Copyright 2020 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//! Implementation of the Syslog trait for Linux.
use std::fs::File;
use std::io::Write;
use std::mem;
use std::os::unix::io::AsRawFd;
use std::os::unix::io::FromRawFd;
use std::os::unix::net::UnixDatagram;
use std::ptr::null;
use libc::closelog;
use libc::fcntl;
use libc::localtime_r;
use libc::openlog;
use libc::time;
use libc::time_t;
use libc::tm;
use libc::F_GETFD;
use libc::LOG_NDELAY;
use libc::LOG_PERROR;
use libc::LOG_PID;
use libc::LOG_USER;
use once_cell::sync::OnceCell;
use super::super::getpid;
use crate::syslog::Error;
use crate::syslog::Facility;
use crate::syslog::Priority;
use crate::syslog::Syslog;
use crate::RawDescriptor;
/// Global syslog socket derived from the fd opened by `openlog()`.
/// This is initialized in `PlatformSyslog::new()`.
static SYSLOG_SOCKET: OnceCell<UnixDatagram> = OnceCell::new();
pub struct PlatformSyslog {}
struct SyslogSocket {}
impl Write for SyslogSocket {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
if let Some(socket) = SYSLOG_SOCKET.get() {
// If `send()` fails, there is nothing we can do about it, so just ignore the result.
let _ = socket.send(buf);
}
// Abandon all hope but this is fine
Ok(buf.len())
}
fn flush(&mut self) -> std::io::Result<()> {
Ok(())
}
}
impl Syslog for PlatformSyslog {
fn new(
proc_name: String,
facility: Facility,
) -> Result<(Option<Box<dyn log::Log + Send>>, Option<RawDescriptor>), Error> {
// Calling openlog_and_get_socket() more than once will cause the previous syslogger FD to
// be closed, invalidating the log::Log object in an unsafe manner. The OnceCell
// get_or_try_init() ensures we only call it once.
//
// b/238923791 is tracking fixing this problem.
let socket = SYSLOG_SOCKET.get_or_try_init(openlog_and_get_socket)?;
let mut builder = env_logger::Builder::new();
// Everything is filtered layer above
builder.filter_level(log::LevelFilter::Trace);
let fd = socket.as_raw_fd();
builder.target(env_logger::Target::Pipe(Box::new(SyslogSocket {})));
builder.format(move |buf, record| {
const MONTHS: [&str; 12] = [
"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
];
let tm = get_localtime();
let pri: Priority = record.level().into();
let prifac = (pri as u8) | (facility as u8);
write!(
buf,
"<{}>{} {:02} {:02}:{:02}:{:02} {}[{}]: ",
prifac,
MONTHS[tm.tm_mon as usize],
tm.tm_mday,
tm.tm_hour,
tm.tm_min,
tm.tm_sec,
&proc_name,
getpid()
)?;
if let Some(path) = record.file() {
write!(buf, " [{}", path)?;
if let Some(line) = record.line() {
write!(buf, ":{}", line)?;
}
write!(buf, "] ")?;
}
writeln!(buf, "{}", record.args())
});
// https://github.com/env-logger-rs/env_logger/issues/208
builder.is_test(true);
Ok((Some(Box::new(builder.build())), Some(fd)))
}
}
// Uses libc's openlog function to get a socket to the syslogger. By getting the socket this way, as
// opposed to connecting to the syslogger directly, libc's internal state gets initialized for other
// libraries (e.g. minijail) that make use of libc's syslog function. Note that this function
// depends on no other threads or signal handlers being active in this process because they might
// create FDs.
//
// TODO(zachr): Once https://android-review.googlesource.com/470998 lands, there won't be any
// libraries in use that hard depend on libc's syslogger. Remove this and go back to making the
// connection directly once minjail is ready.
fn openlog_and_get_socket() -> Result<UnixDatagram, Error> {
// SAFETY:
// closelog first in case there was already a file descriptor open. Safe because it takes no
// arguments and just closes an open file descriptor. Does nothing if the file descriptor
// was not already open.
unsafe {
closelog();
}
// Ordinarily libc's FD for the syslog connection can't be accessed, but we can guess that the
// FD that openlog will be getting is the lowest unused FD. To guarantee that an FD is opened in
// this function we use the LOG_NDELAY to tell openlog to connect to the syslog now. To get the
// lowest unused FD, we open a dummy file (which the manual says will always return the lowest
// fd), and then close that fd. Voilà, we now know the lowest numbered FD. The call to openlog
// will make use of that FD, and then we just wrap a `UnixDatagram` around it for ease of use.
let fd = File::open("/dev/null")
.map_err(Error::GetLowestFd)?
.as_raw_fd();
// SAFETY: See comments for each unsafe line in the block.
unsafe {
// Safe because openlog accesses no pointers because `ident` is null, only valid flags are
// used, and it returns no error.
openlog(null(), LOG_NDELAY | LOG_PERROR | LOG_PID, LOG_USER);
// For safety, ensure the fd we guessed is valid. The `fcntl` call itself only reads the
// file descriptor table of the current process, which is trivially safe.
if fcntl(fd, F_GETFD) >= 0 {
Ok(UnixDatagram::from_raw_fd(fd))
} else {
Err(Error::InvalidFd)
}
}
}
fn get_localtime() -> tm {
// SAFETY: See comments for each unsafe line in the block.
unsafe {
// Safe because tm is just a struct of plain data.
let mut tm: tm = mem::zeroed();
let mut now: time_t = 0;
// Safe because we give time a valid pointer and can never fail.
time(&mut now as *mut _);
// Safe because we give localtime_r valid pointers and can never fail.
localtime_r(&now, &mut tm as *mut _);
tm
}
}