Skip to content

Commit 47c0f42

Browse files
tisonkunSteveLauC
andauthored
Add syslog supports (#2537)
* Stub for wrap syslog.h Signed-off-by: tison <[email protected]> * Wrap openlog Signed-off-by: tison <[email protected]> * Make it work Signed-off-by: tison <[email protected]> * lib_enums and docs Signed-off-by: tison <[email protected]> * wrap priority Signed-off-by: tison <[email protected]> * reduce error handle noise and add docs Signed-off-by: tison <[email protected]> * try add setlogmask Signed-off-by: tison <[email protected]> * Revert "try add setlogmask" This reverts commit 98289d5. * fixup tests Signed-off-by: tison <[email protected]> * fixup all Signed-off-by: tison <[email protected]> * add changelog Signed-off-by: tison <[email protected]> * turn on syslog for tests Signed-off-by: tison <[email protected]> * revert irrelevant format Signed-off-by: tison <[email protected]> * add new feature flags Signed-off-by: tison <[email protected]> * Add back closelog Signed-off-by: tison <[email protected]> * Use AsRef<OsStr> for openlog.ident Signed-off-by: tison <[email protected]> * polish APIs Signed-off-by: tison <[email protected]> * fixup Signed-off-by: tison <[email protected]> * test on linux Signed-off-by: tison <[email protected]> * test on unix Signed-off-by: tison <[email protected]> * fixup flags Signed-off-by: tison <[email protected]> * fixup all Signed-off-by: tison <[email protected]> * Update src/syslog.rs Co-authored-by: SteveLauC <[email protected]> * refactor: use AsRef<OsStr> on non-Linux and &'static CStr on Linux for ident arg of openlog * style: fmt * fix: Linux build by importing CStr --------- Signed-off-by: tison <[email protected]> Co-authored-by: SteveLauC <[email protected]>
1 parent 7452b68 commit 47c0f42

File tree

6 files changed

+266
-1
lines changed

6 files changed

+266
-1
lines changed

Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ resource = []
6464
sched = ["process"]
6565
signal = ["process"]
6666
socket = ["memoffset"]
67+
syslog = []
6768
term = []
6869
time = []
6970
ucontext = ["signal"]
@@ -80,7 +81,7 @@ semver = "1.0.7"
8081
nix = { path = ".", features = ["acct", "aio", "dir", "env", "event", "fanotify",
8182
"feature", "fs", "hostname", "inotify", "ioctl", "kmod", "mman", "mount", "mqueue",
8283
"net", "personality", "poll", "pthread", "ptrace", "quota", "process", "reboot",
83-
"resource", "sched", "signal", "socket", "term", "time", "ucontext", "uio",
84+
"resource", "sched", "signal", "socket", "syslog", "term", "time", "ucontext", "uio",
8485
"user", "zerocopy"] }
8586

8687
[target.'cfg(any(target_os = "android", target_os = "linux"))'.dev-dependencies]

changelog/2537.added.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add support for `syslog`, `openlog`, `closelog` on all `unix`.

src/lib.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
//! * `sched` - Manipulate process's scheduling
3535
//! * `socket` - Sockets, whether for networking or local use
3636
//! * `signal` - Send and receive signals to processes
37+
//! * `syslog` - System logging
3738
//! * `term` - Terminal control APIs
3839
//! * `time` - Query the operating system's clocks
3940
//! * `ucontext` - User thread context
@@ -79,6 +80,7 @@
7980
feature = "sched",
8081
feature = "socket",
8182
feature = "signal",
83+
feature = "syslog",
8284
feature = "term",
8385
feature = "time",
8486
feature = "ucontext",
@@ -200,6 +202,11 @@ feature! {
200202
pub mod spawn;
201203
}
202204

205+
feature! {
206+
#![feature = "syslog"]
207+
pub mod syslog;
208+
}
209+
203210
use std::ffi::{CStr, CString, OsStr};
204211
use std::mem::MaybeUninit;
205212
use std::os::unix::ffi::OsStrExt;

src/syslog.rs

Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
//! Interfaces for controlling system log.
2+
3+
use crate::{NixPath, Result};
4+
use std::ffi::OsStr;
5+
use std::ptr;
6+
7+
/// Logging options of subsequent [`syslog`] calls can be set by calling [`openlog`].
8+
///
9+
/// The parameter `ident` is a string that will be prepended to every message. The `logopt`
10+
/// argument specifies logging options. The `facility` parameter encodes a default facility to be
11+
/// assigned to all messages that do not have an explicit facility encoded.
12+
//
13+
// On Linux, the `ident` argument needs to have static lifetime according to the
14+
// man page:
15+
//
16+
// The argument ident in the call of openlog() is probably stored as-is. Thus,
17+
// if the string it points to is changed, syslog() may start prepending the changed
18+
// string, and if the string it points to ceases to exist, the results are
19+
// undefined. Most portable is to use a string constant.
20+
#[cfg(target_os = "linux")]
21+
pub fn openlog(
22+
ident: Option<&'static std::ffi::CStr>,
23+
logopt: LogFlags,
24+
facility: Facility,
25+
) -> Result<()> {
26+
let logopt = logopt.bits();
27+
let facility = facility as libc::c_int;
28+
match ident {
29+
None => unsafe {
30+
libc::openlog(ptr::null(), logopt, facility);
31+
},
32+
Some(ident) => ident.with_nix_path(|ident| unsafe {
33+
libc::openlog(ident.as_ptr(), logopt, facility);
34+
})?,
35+
}
36+
37+
Ok(())
38+
}
39+
40+
/// Logging options of subsequent [`syslog`] calls can be set by calling [`openlog`].
41+
///
42+
/// The parameter `ident` is a string that will be prepended to every message. The `logopt`
43+
/// argument specifies logging options. The `facility` parameter encodes a default facility to be
44+
/// assigned to all messages that do not have an explicit facility encoded.
45+
#[cfg(not(target_os = "linux"))]
46+
pub fn openlog<S: AsRef<OsStr> + ?Sized>(
47+
ident: Option<&S>,
48+
logopt: LogFlags,
49+
facility: Facility,
50+
) -> Result<()> {
51+
let logopt = logopt.bits();
52+
let facility = facility as libc::c_int;
53+
match ident.map(OsStr::new) {
54+
None => unsafe {
55+
libc::openlog(ptr::null(), logopt, facility);
56+
},
57+
Some(ident) => ident.with_nix_path(|ident| unsafe {
58+
libc::openlog(ident.as_ptr(), logopt, facility);
59+
})?,
60+
}
61+
62+
Ok(())
63+
}
64+
65+
/// Writes message to the system message logger.
66+
///
67+
/// The message is then written to the system console, log files, logged-in users, or forwarded
68+
/// to other machines as appropriate.
69+
///
70+
/// # Examples
71+
///
72+
/// ```rust
73+
/// use nix::syslog::{openlog, syslog, Facility, LogFlags, Severity, Priority};
74+
///
75+
/// let priority = Priority::new(Severity::LOG_EMERG, Facility::LOG_USER);
76+
/// syslog(priority, "Hello, nix!").unwrap();
77+
///
78+
/// // use `format!` to format the message
79+
/// let name = "syslog";
80+
/// syslog(priority, &format!("Hello, {name}!")).unwrap();
81+
/// ```
82+
pub fn syslog<P, S>(priority: P, message: &S) -> Result<()>
83+
where
84+
P: Into<Priority>,
85+
S: AsRef<OsStr> + ?Sized,
86+
{
87+
let priority = priority.into();
88+
let formatter = OsStr::new("%s");
89+
let message = OsStr::new(message);
90+
formatter.with_nix_path(|formatter| {
91+
message.with_nix_path(|message| unsafe {
92+
libc::syslog(priority.0, formatter.as_ptr(), message.as_ptr())
93+
})
94+
})??;
95+
Ok(())
96+
}
97+
98+
/// Closes the log file.
99+
pub fn closelog() {
100+
unsafe { libc::closelog() }
101+
}
102+
103+
/// The priority for a log message.
104+
#[derive(Debug, Clone, Copy)]
105+
pub struct Priority(libc::c_int);
106+
107+
impl Priority {
108+
/// Create a new priority from a facility and severity level.
109+
pub fn new(severity: Severity, facility: Facility) -> Self {
110+
let priority = (facility as libc::c_int) | (severity as libc::c_int);
111+
Priority(priority)
112+
}
113+
}
114+
115+
impl From<Severity> for Priority {
116+
fn from(severity: Severity) -> Self {
117+
let priority = severity as libc::c_int;
118+
Priority(priority)
119+
}
120+
}
121+
122+
libc_bitflags! {
123+
/// Options for system logging.
124+
pub struct LogFlags: libc::c_int {
125+
/// Log the process id with each message: useful for identifying instantiations of
126+
/// daemons.
127+
LOG_PID;
128+
/// If syslog() cannot pass the message to syslogd(8) it will attempt to write the
129+
/// message to the console ("/dev/console").
130+
LOG_CONS;
131+
/// The converse of [`LOG_NDELAY`][LogFlags::LOG_NDELAY]; opening of the connection is
132+
/// delayed until `syslog` is called.
133+
///
134+
/// This is the default, and need not be specified.
135+
LOG_ODELAY;
136+
/// Open the connection to syslogd(8) immediately. Normally the open is delayed until
137+
/// the first message is logged. Useful for programs that need to manage the order in
138+
/// which file descriptors are allocated.
139+
LOG_NDELAY;
140+
/// Write the message to standard error output as well to the system log.
141+
#[cfg(not(any(target_os = "redox", target_os = "illumos")))]
142+
LOG_PERROR;
143+
}
144+
}
145+
146+
libc_enum! {
147+
/// Severity levels for log messages.
148+
#[repr(i32)]
149+
#[non_exhaustive]
150+
pub enum Severity {
151+
/// A panic condition.
152+
///
153+
/// This is normally broadcast to all users.
154+
LOG_EMERG,
155+
/// A condition that should be corrected immediately, such as a corrupted system database.
156+
LOG_ALERT,
157+
/// Critical conditions, e.g., hard device errors.
158+
LOG_CRIT,
159+
/// Errors.
160+
LOG_ERR,
161+
/// Warning messages.
162+
LOG_WARNING,
163+
/// Conditions that are not error conditions, but should possibly be handled specially.
164+
LOG_NOTICE,
165+
/// Informational messages.
166+
LOG_INFO,
167+
/// Messages that contain information normally of use only when debugging a program.
168+
LOG_DEBUG,
169+
}
170+
}
171+
172+
libc_enum! {
173+
/// Facilities for log messages.
174+
#[repr(i32)]
175+
#[non_exhaustive]
176+
pub enum Facility {
177+
/// Messages generated by the kernel.
178+
///
179+
/// These cannot be generated by any user processes.
180+
LOG_KERN,
181+
/// Messages generated by random user processes.
182+
///
183+
/// This is the default facility identifier if none is specified.
184+
LOG_USER,
185+
/// The mail system.
186+
LOG_MAIL,
187+
/// System daemons, such as routed(8), that are not provided for explicitly by other facilities.
188+
LOG_DAEMON,
189+
/// The authorization system: login(1), su(1), getty(8), etc.
190+
LOG_AUTH,
191+
/// Messages generated internally by syslogd(8).
192+
LOG_SYSLOG,
193+
/// The line printer spooling system: cups-lpd(8), cupsd(8), etc.
194+
LOG_LPR,
195+
/// The network news system.
196+
LOG_NEWS,
197+
/// The uucp system.
198+
LOG_UUCP,
199+
/// Reserved for local use.
200+
LOG_LOCAL0,
201+
/// Reserved for local use.
202+
LOG_LOCAL1,
203+
/// Reserved for local use.
204+
LOG_LOCAL2,
205+
/// Reserved for local use.
206+
LOG_LOCAL3,
207+
/// Reserved for local use.
208+
LOG_LOCAL4,
209+
/// Reserved for local use.
210+
LOG_LOCAL5,
211+
/// Reserved for local use.
212+
LOG_LOCAL6,
213+
/// Reserved for local use.
214+
LOG_LOCAL7,
215+
}
216+
}

test/test.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ mod test_sendfile;
4242
))]
4343
mod test_spawn;
4444

45+
mod test_syslog;
46+
4547
mod test_time;
4648
mod test_unistd;
4749

test/test_syslog.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
use nix::syslog::{openlog, syslog, Facility, LogFlags, Severity};
2+
3+
#[test]
4+
fn test_syslog_hello_world() {
5+
let flags = LogFlags::LOG_PID;
6+
7+
#[cfg(not(target_os = "linux"))]
8+
openlog(None::<&str>, flags, Facility::LOG_USER).unwrap();
9+
#[cfg(target_os = "linux")]
10+
openlog(None, flags, Facility::LOG_USER).unwrap();
11+
12+
syslog(Severity::LOG_EMERG, "Hello, nix!").unwrap();
13+
let name = "syslog";
14+
syslog(Severity::LOG_NOTICE, &format!("Hello, {name}!")).unwrap();
15+
}
16+
17+
#[test]
18+
#[cfg(target_os = "linux")]
19+
fn test_openlog_with_ident() {
20+
use std::ffi::CStr;
21+
22+
const IDENT: &CStr = unsafe {
23+
CStr::from_bytes_with_nul_unchecked(b"test_openlog_with_ident\0")
24+
};
25+
26+
let flags = LogFlags::LOG_PID;
27+
openlog(Some(IDENT), flags, Facility::LOG_USER).unwrap();
28+
syslog(Severity::LOG_EMERG, "Hello, ident!").unwrap();
29+
}
30+
31+
#[test]
32+
#[cfg(not(target_os = "linux"))]
33+
fn test_openlog_with_ident() {
34+
let flags = LogFlags::LOG_PID;
35+
openlog(Some("test_openlog_with_ident"), flags, Facility::LOG_USER)
36+
.unwrap();
37+
syslog(Severity::LOG_EMERG, "Hello, ident!").unwrap();
38+
}

0 commit comments

Comments
 (0)