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