relibc/header/sys_syslog/
logger.rs1use alloc::{borrow::ToOwned, string::String};
2use core::{ffi::VaList, ptr::null_mut};
3
4use crate::{
5 c_str::CStr,
6 error::Result,
7 header::{
8 stdio::{fprintf, printf::printf, stderr},
9 time::time,
10 unistd::getpid,
11 },
12 io::Write,
13 platform::{
14 self,
15 types::{c_char, c_int},
16 },
17 sync::Mutex,
18};
19
20use bitflags::bitflags;
21use chrono::{DateTime, Utc};
22
23use super::{
24 LOG_ALERT, LOG_AUTH, LOG_AUTHPRIV, LOG_CONS, LOG_CRIT, LOG_CRON, LOG_DAEMON, LOG_DEBUG,
25 LOG_EMERG, LOG_ERR, LOG_FTP, LOG_INFO, LOG_KERN, LOG_LOCAL0, LOG_LOCAL1, LOG_LOCAL2,
26 LOG_LOCAL3, LOG_LOCAL4, LOG_LOCAL5, LOG_LOCAL6, LOG_LOCAL7, LOG_LPR, LOG_MAIL, LOG_MASK,
27 LOG_NDELAY, LOG_NEWS, LOG_NOTICE, LOG_NOWAIT, LOG_ODELAY, LOG_PERROR, LOG_PID, LOG_SYSLOG,
28 LOG_UPTO, LOG_USER, LOG_UUCP, LOG_WARNING, sys::LogFile,
29};
30
31pub(super) static LOGGER: Mutex<LogParams<LogFile>> = Mutex::new(LogParams::new(None));
32
33pub(super) struct LogParams<L: LogSink> {
34 ident: String,
37 pub opt: Config,
38 pub mask: Priority,
39 writer: Option<L>,
40}
41
42impl<L: LogSink> LogParams<L> {
43 pub const fn new(writer: Option<L>) -> Self {
44 LogParams {
45 ident: String::new(),
46 opt: Config::DelayOpen,
47 mask: Priority::from_bits_truncate(Priority::User.bits() | Priority::UpToDebug.bits()),
48 writer,
49 }
50 }
51
52 pub fn write_log(&mut self, priority: Priority, message: CStr<'_>, ap: VaList) {
53 if message.is_empty() {
54 return;
55 }
56 if self.ident.is_empty() {
57 self.set_identity(None);
58 }
59
60 let epoch = unsafe { time(null_mut()) };
61 let currtime: DateTime<Utc> = DateTime::from_timestamp(epoch, 0).unwrap_or_default();
62 let currtime_s = currtime.format("%b %e %T %Y");
63 let pid = self.opt.contains(Config::Pid).then(|| getpid());
64
65 let mut buffer = if let Some(pid) = pid {
68 format!(
69 "<{}>{} {}{}: ",
70 priority.bits(),
71 currtime_s,
72 self.ident,
73 pid
74 )
75 .into_bytes()
76 } else {
77 format!("<{}>{} {}: ", priority.bits(), currtime_s, self.ident).into_bytes()
78 };
79 let prefix = buffer.len();
80
81 unsafe { printf(&mut buffer, message, ap) };
85 buffer.extend(b"\n\0");
86
87 if self.maybe_open_logger().is_ok()
88 && self
89 .writer
90 .as_mut()
91 .map(|w| w.writer().write_all(&buffer).is_err())
92 .unwrap_or(true)
93 {
94 if !(self.open_logger().is_ok()
96 && self
97 .writer
98 .as_mut()
99 .and_then(|w| w.writer().write_all(&buffer).ok())
100 .is_some())
101 && self.opt.contains(Config::Console)
102 {
103 }
105 }
106 if self.opt.contains(Config::PError) {
107 unsafe {
111 fprintf(
113 stderr,
114 c"%s: %s".as_ptr(),
115 self.ident.as_ptr().cast::<c_char>(),
116 buffer[prefix..].as_ptr().cast::<c_char>(),
117 );
118 }
119 }
120 }
121
122 pub fn set_identity_cstr(&mut self, ident: Option<CStr<'_>>) {
126 let ident = ident
127 .and_then(|ident| (!ident.is_empty()).then(|| ident.to_str().ok()))
128 .flatten();
129 self.set_identity(ident);
130 }
131
132 pub fn set_identity(&mut self, ident: Option<&str>) {
137 self.ident = ident
138 .map(|ident| {
139 let ident = ident.bytes().chain([0]).collect();
140 unsafe { String::from_utf8_unchecked(ident) }
142 })
143 .unwrap_or_else(|| {
144 unsafe { CStr::from_nullable_ptr(platform::program_invocation_short_name) }
145 .and_then(|name| {
146 let name = name.to_str().ok()?.bytes().chain([0]).collect();
147 Some(unsafe { String::from_utf8_unchecked(name) })
149 })
150 .unwrap_or_else(|| "\0".to_owned())
151 });
152 }
153
154 pub fn maybe_open_logger(&mut self) -> Result<()> {
156 if self.writer.is_none() {
157 self.open_logger()
158 } else {
159 Ok(())
160 }
161 }
162
163 pub fn open_logger(&mut self) -> Result<()> {
165 L::open().map(|file| {
166 self.writer.replace(file);
167 })
168 }
169
170 pub fn close(&mut self) {
172 self.writer.take();
173 }
174}
175
176pub(super) trait LogSink {
178 type Sink: Write;
179
180 fn open() -> Result<Self>
181 where
182 Self: Sized;
183
184 fn writer(&mut self) -> &mut Self::Sink;
185}
186
187bitflags! {
188 #[derive(Clone, Copy)]
189 pub struct Config: c_int {
190 const Pid = LOG_PID;
191 const Console = LOG_CONS;
192 const DelayOpen = LOG_ODELAY;
193 const NoDelay = LOG_NDELAY;
194 const NoWait = LOG_NOWAIT;
195 const PError = LOG_PERROR;
196 }
197}
198
199bitflags! {
200 #[derive(Clone, Copy)]
202 pub struct Priority: c_int {
203 const Emerg = LOG_EMERG;
204 const Alert = LOG_ALERT;
205 const Crit = LOG_CRIT;
206 const Err = LOG_ERR;
207 const Warn = LOG_WARNING;
208 const Notice = LOG_NOTICE;
209 const Info = LOG_INFO;
210 const Debug = LOG_DEBUG;
211
212 const UpToEmerg = LOG_UPTO(LOG_EMERG);
213 const UpToAlert = LOG_UPTO(LOG_ALERT);
214 const UpToCrit = LOG_UPTO(LOG_CRIT);
215 const UpToErr = LOG_UPTO(LOG_ERR);
216 const UpToWarn = LOG_UPTO(LOG_WARNING);
217 const UpToNotice = LOG_UPTO(LOG_NOTICE);
218 const UpToInfo = LOG_UPTO(LOG_INFO);
219 const UpToDebug = LOG_UPTO(LOG_DEBUG);
220
221 const Kern = LOG_KERN;
222 const User = LOG_USER;
223 const Mail = LOG_MAIL;
224 const Daemon = LOG_DAEMON;
225 const Auth = LOG_AUTH;
226 const Syslog = LOG_SYSLOG;
227 const Printer = LOG_LPR;
228 const News = LOG_NEWS;
229 const UUCP = LOG_UUCP;
230 const CRON = LOG_CRON;
231 const AuthPriv = LOG_AUTHPRIV;
232 const FTP = LOG_FTP;
233 const Local0 = LOG_LOCAL0;
234 const Local1 = LOG_LOCAL1;
235 const Local2 = LOG_LOCAL2;
236 const Local3 = LOG_LOCAL3;
237 const Local4 = LOG_LOCAL4;
238 const Local5 = LOG_LOCAL5;
239 const Local6 = LOG_LOCAL6;
240 const Local7 = LOG_LOCAL7;
241
242 const FacilityMask = 0x3ff;
244 const PriorityMask = !Self::FacilityMask.bits();
245 }
246}
247
248impl Priority {
249 pub fn with_mask(self, mask: c_int) -> Option<Self> {
251 let mask = Self::from_bits(mask)? & Self::FacilityMask;
253 let facility = self & Self::PriorityMask;
254 Some(mask | facility)
255 }
256
257 pub fn with_facility(self, facility: c_int) -> Option<Self> {
259 let facility = Self::from_bits(facility)? & Self::PriorityMask;
261 let mask = self & Self::FacilityMask;
262 Some(mask | facility)
263 }
264
265 pub fn should_log(self, priority: Self) -> bool {
267 (self & Self::FacilityMask)
268 .contains(Priority::from_bits_truncate(LOG_MASK(priority.bits())))
269 }
270}