Skip to content

Commit

Permalink
Add syslog supports (nix-rust#2537)
Browse files Browse the repository at this point in the history
* Stub for wrap syslog.h

Signed-off-by: tison <wander4096@gmail.com>

* Wrap openlog

Signed-off-by: tison <wander4096@gmail.com>

* Make it work

Signed-off-by: tison <wander4096@gmail.com>

* lib_enums and docs

Signed-off-by: tison <wander4096@gmail.com>

* wrap priority

Signed-off-by: tison <wander4096@gmail.com>

* reduce error handle noise and add docs

Signed-off-by: tison <wander4096@gmail.com>

* try add setlogmask

Signed-off-by: tison <wander4096@gmail.com>

* Revert "try add setlogmask"

This reverts commit 98289d5.

* fixup tests

Signed-off-by: tison <wander4096@gmail.com>

* fixup all

Signed-off-by: tison <wander4096@gmail.com>

* add changelog

Signed-off-by: tison <wander4096@gmail.com>

* turn on syslog for tests

Signed-off-by: tison <wander4096@gmail.com>

* revert irrelevant format

Signed-off-by: tison <wander4096@gmail.com>

* add new feature flags

Signed-off-by: tison <wander4096@gmail.com>

* Add back closelog

Signed-off-by: tison <wander4096@gmail.com>

* Use AsRef<OsStr> for openlog.ident

Signed-off-by: tison <wander4096@gmail.com>

* polish APIs

Signed-off-by: tison <wander4096@gmail.com>

* fixup

Signed-off-by: tison <wander4096@gmail.com>

* test on linux

Signed-off-by: tison <wander4096@gmail.com>

* test on unix

Signed-off-by: tison <wander4096@gmail.com>

* fixup flags

Signed-off-by: tison <wander4096@gmail.com>

* fixup all

Signed-off-by: tison <wander4096@gmail.com>

* Update src/syslog.rs

Co-authored-by: SteveLauC <stevelauc@outlook.com>

* 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 <wander4096@gmail.com>
Co-authored-by: SteveLauC <stevelauc@outlook.com>
  • Loading branch information
tisonkun and SteveLauC authored Nov 17, 2024
1 parent 7452b68 commit 47c0f42
Show file tree
Hide file tree
Showing 6 changed files with 266 additions and 1 deletion.
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ resource = []
sched = ["process"]
signal = ["process"]
socket = ["memoffset"]
syslog = []
term = []
time = []
ucontext = ["signal"]
Expand All @@ -80,7 +81,7 @@ semver = "1.0.7"
nix = { path = ".", features = ["acct", "aio", "dir", "env", "event", "fanotify",
"feature", "fs", "hostname", "inotify", "ioctl", "kmod", "mman", "mount", "mqueue",
"net", "personality", "poll", "pthread", "ptrace", "quota", "process", "reboot",
"resource", "sched", "signal", "socket", "term", "time", "ucontext", "uio",
"resource", "sched", "signal", "socket", "syslog", "term", "time", "ucontext", "uio",
"user", "zerocopy"] }

[target.'cfg(any(target_os = "android", target_os = "linux"))'.dev-dependencies]
Expand Down
1 change: 1 addition & 0 deletions changelog/2537.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add support for `syslog`, `openlog`, `closelog` on all `unix`.
7 changes: 7 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
//! * `sched` - Manipulate process's scheduling
//! * `socket` - Sockets, whether for networking or local use
//! * `signal` - Send and receive signals to processes
//! * `syslog` - System logging
//! * `term` - Terminal control APIs
//! * `time` - Query the operating system's clocks
//! * `ucontext` - User thread context
Expand Down Expand Up @@ -79,6 +80,7 @@
feature = "sched",
feature = "socket",
feature = "signal",
feature = "syslog",
feature = "term",
feature = "time",
feature = "ucontext",
Expand Down Expand Up @@ -200,6 +202,11 @@ feature! {
pub mod spawn;
}

feature! {
#![feature = "syslog"]
pub mod syslog;
}

use std::ffi::{CStr, CString, OsStr};
use std::mem::MaybeUninit;
use std::os::unix::ffi::OsStrExt;
Expand Down
216 changes: 216 additions & 0 deletions src/syslog.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
//! Interfaces for controlling system log.
use crate::{NixPath, Result};
use std::ffi::OsStr;
use std::ptr;

/// Logging options of subsequent [`syslog`] calls can be set by calling [`openlog`].
///
/// The parameter `ident` is a string that will be prepended to every message. The `logopt`
/// argument specifies logging options. The `facility` parameter encodes a default facility to be
/// assigned to all messages that do not have an explicit facility encoded.
//
// On Linux, the `ident` argument needs to have static lifetime according to the
// man page:
//
// The argument ident in the call of openlog() is probably stored as-is. Thus,
// if the string it points to is changed, syslog() may start prepending the changed
// string, and if the string it points to ceases to exist, the results are
// undefined. Most portable is to use a string constant.
#[cfg(target_os = "linux")]
pub fn openlog(
ident: Option<&'static std::ffi::CStr>,
logopt: LogFlags,
facility: Facility,
) -> Result<()> {
let logopt = logopt.bits();
let facility = facility as libc::c_int;
match ident {
None => unsafe {
libc::openlog(ptr::null(), logopt, facility);
},
Some(ident) => ident.with_nix_path(|ident| unsafe {
libc::openlog(ident.as_ptr(), logopt, facility);
})?,
}

Ok(())
}

/// Logging options of subsequent [`syslog`] calls can be set by calling [`openlog`].
///
/// The parameter `ident` is a string that will be prepended to every message. The `logopt`
/// argument specifies logging options. The `facility` parameter encodes a default facility to be
/// assigned to all messages that do not have an explicit facility encoded.
#[cfg(not(target_os = "linux"))]
pub fn openlog<S: AsRef<OsStr> + ?Sized>(
ident: Option<&S>,
logopt: LogFlags,
facility: Facility,
) -> Result<()> {
let logopt = logopt.bits();
let facility = facility as libc::c_int;
match ident.map(OsStr::new) {
None => unsafe {
libc::openlog(ptr::null(), logopt, facility);
},
Some(ident) => ident.with_nix_path(|ident| unsafe {
libc::openlog(ident.as_ptr(), logopt, facility);
})?,
}

Ok(())
}

/// Writes message to the system message logger.
///
/// The message is then written to the system console, log files, logged-in users, or forwarded
/// to other machines as appropriate.
///
/// # Examples
///
/// ```rust
/// use nix::syslog::{openlog, syslog, Facility, LogFlags, Severity, Priority};
///
/// let priority = Priority::new(Severity::LOG_EMERG, Facility::LOG_USER);
/// syslog(priority, "Hello, nix!").unwrap();
///
/// // use `format!` to format the message
/// let name = "syslog";
/// syslog(priority, &format!("Hello, {name}!")).unwrap();
/// ```
pub fn syslog<P, S>(priority: P, message: &S) -> Result<()>
where
P: Into<Priority>,
S: AsRef<OsStr> + ?Sized,
{
let priority = priority.into();
let formatter = OsStr::new("%s");
let message = OsStr::new(message);
formatter.with_nix_path(|formatter| {
message.with_nix_path(|message| unsafe {
libc::syslog(priority.0, formatter.as_ptr(), message.as_ptr())
})
})??;
Ok(())
}

/// Closes the log file.
pub fn closelog() {
unsafe { libc::closelog() }
}

/// The priority for a log message.
#[derive(Debug, Clone, Copy)]
pub struct Priority(libc::c_int);

impl Priority {
/// Create a new priority from a facility and severity level.
pub fn new(severity: Severity, facility: Facility) -> Self {
let priority = (facility as libc::c_int) | (severity as libc::c_int);
Priority(priority)
}
}

impl From<Severity> for Priority {
fn from(severity: Severity) -> Self {
let priority = severity as libc::c_int;
Priority(priority)
}
}

libc_bitflags! {
/// Options for system logging.
pub struct LogFlags: libc::c_int {
/// Log the process id with each message: useful for identifying instantiations of
/// daemons.
LOG_PID;
/// If syslog() cannot pass the message to syslogd(8) it will attempt to write the
/// message to the console ("/dev/console").
LOG_CONS;
/// The converse of [`LOG_NDELAY`][LogFlags::LOG_NDELAY]; opening of the connection is
/// delayed until `syslog` is called.
///
/// This is the default, and need not be specified.
LOG_ODELAY;
/// Open the connection to syslogd(8) immediately. Normally the open is delayed until
/// the first message is logged. Useful for programs that need to manage the order in
/// which file descriptors are allocated.
LOG_NDELAY;
/// Write the message to standard error output as well to the system log.
#[cfg(not(any(target_os = "redox", target_os = "illumos")))]
LOG_PERROR;
}
}

libc_enum! {
/// Severity levels for log messages.
#[repr(i32)]
#[non_exhaustive]
pub enum Severity {
/// A panic condition.
///
/// This is normally broadcast to all users.
LOG_EMERG,
/// A condition that should be corrected immediately, such as a corrupted system database.
LOG_ALERT,
/// Critical conditions, e.g., hard device errors.
LOG_CRIT,
/// Errors.
LOG_ERR,
/// Warning messages.
LOG_WARNING,
/// Conditions that are not error conditions, but should possibly be handled specially.
LOG_NOTICE,
/// Informational messages.
LOG_INFO,
/// Messages that contain information normally of use only when debugging a program.
LOG_DEBUG,
}
}

libc_enum! {
/// Facilities for log messages.
#[repr(i32)]
#[non_exhaustive]
pub enum Facility {
/// Messages generated by the kernel.
///
/// These cannot be generated by any user processes.
LOG_KERN,
/// Messages generated by random user processes.
///
/// This is the default facility identifier if none is specified.
LOG_USER,
/// The mail system.
LOG_MAIL,
/// System daemons, such as routed(8), that are not provided for explicitly by other facilities.
LOG_DAEMON,
/// The authorization system: login(1), su(1), getty(8), etc.
LOG_AUTH,
/// Messages generated internally by syslogd(8).
LOG_SYSLOG,
/// The line printer spooling system: cups-lpd(8), cupsd(8), etc.
LOG_LPR,
/// The network news system.
LOG_NEWS,
/// The uucp system.
LOG_UUCP,
/// Reserved for local use.
LOG_LOCAL0,
/// Reserved for local use.
LOG_LOCAL1,
/// Reserved for local use.
LOG_LOCAL2,
/// Reserved for local use.
LOG_LOCAL3,
/// Reserved for local use.
LOG_LOCAL4,
/// Reserved for local use.
LOG_LOCAL5,
/// Reserved for local use.
LOG_LOCAL6,
/// Reserved for local use.
LOG_LOCAL7,
}
}
2 changes: 2 additions & 0 deletions test/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ mod test_sendfile;
))]
mod test_spawn;

mod test_syslog;

mod test_time;
mod test_unistd;

Expand Down
38 changes: 38 additions & 0 deletions test/test_syslog.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
use nix::syslog::{openlog, syslog, Facility, LogFlags, Severity};

#[test]
fn test_syslog_hello_world() {
let flags = LogFlags::LOG_PID;

#[cfg(not(target_os = "linux"))]
openlog(None::<&str>, flags, Facility::LOG_USER).unwrap();
#[cfg(target_os = "linux")]
openlog(None, flags, Facility::LOG_USER).unwrap();

syslog(Severity::LOG_EMERG, "Hello, nix!").unwrap();
let name = "syslog";
syslog(Severity::LOG_NOTICE, &format!("Hello, {name}!")).unwrap();
}

#[test]
#[cfg(target_os = "linux")]
fn test_openlog_with_ident() {
use std::ffi::CStr;

const IDENT: &CStr = unsafe {
CStr::from_bytes_with_nul_unchecked(b"test_openlog_with_ident\0")
};

let flags = LogFlags::LOG_PID;
openlog(Some(IDENT), flags, Facility::LOG_USER).unwrap();
syslog(Severity::LOG_EMERG, "Hello, ident!").unwrap();
}

#[test]
#[cfg(not(target_os = "linux"))]
fn test_openlog_with_ident() {
let flags = LogFlags::LOG_PID;
openlog(Some("test_openlog_with_ident"), flags, Facility::LOG_USER)
.unwrap();
syslog(Severity::LOG_EMERG, "Hello, ident!").unwrap();
}

0 comments on commit 47c0f42

Please sign in to comment.