Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add syslog supports #2537

Merged
merged 26 commits into from
Nov 17, 2024
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 = []
tisonkun marked this conversation as resolved.
Show resolved Hide resolved
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
193 changes: 193 additions & 0 deletions src/syslog.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
//! Interfaces for controlling system log.

use crate::NixPath;
use crate::Result;
use std::ffi::OsStr;

/// 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.
pub fn openlog<S>(
ident: Option<&S>,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This interface should be good for UNIXs other than Linux, since Linux would store the ident pointer as-is (will be used during whole process lifetime), we need to add a separate interface for Linux, and gate this with #[cfg(not(target_os = "linux"))].

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And thanks for adding the Option here! I didn't catch it in my first round of review.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd suggest we remove the ident param for now.

It's not easy to just add Option<&'static str> because it may not end with nul and I don't know how to achieve this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't observe the ident on macos and linux, perhaps this is because the allocated string is collected 🤣

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe Option<&'static CStr>. Let me try.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nix does not pursue uniform interfaces across OSes, if they are different, let them be different. I suggest keeping the original interface for non-Linux platforms as they are not constrained by this limit and adding a separate interface for Linux

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you send a patch to this PR or just edit this PR with the suggested change?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you send a patch to this PR

Yeah, let me send a commit

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See 596280b

logopt: LogFlags,
facility: Facility,
) -> Result<()>
where
S: AsRef<OsStr> + ?Sized,
{
let logopt = logopt.bits();
let facility = facility as libc::c_int;
match ident.map(OsStr::new) {
None => unsafe {
libc::openlog(std::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};
///
/// #[cfg(not(target_os = "haiku"))]
/// let flags = LogFlags::LOG_PID;
/// #[cfg(target_os = "haiku")]
/// let flags = LogFlags::empty();
///
/// openlog(None::<&str>, flags, Facility::LOG_USER).unwrap();
/// syslog(Severity::LOG_EMERG, "Hello, nix!").unwrap();
///
/// // use `format!` to format the message
/// let name = "syslog";
/// syslog(Severity::LOG_EMERG, &format!("Hello, {name}!")).unwrap();
/// ```
pub fn syslog<P, S>(priority: P, message: &S) -> Result<()>
where
P: Into<Priority>,
tisonkun marked this conversation as resolved.
Show resolved Hide resolved
S: AsRef<OsStr> + ?Sized,
{
let priority = priority.into();
let formatter = OsStr::new("%s");
tisonkun marked this conversation as resolved.
Show resolved Hide resolved
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 {
tisonkun marked this conversation as resolved.
Show resolved Hide resolved
/// Log the process id with each message: useful for identifying instantiations of
/// daemons.
#[cfg(not(target_os = "haiku"))]
LOG_PID;
/// If syslog() cannot pass the message to syslogd(8) it will attempt to write the
/// message to the console ("/dev/console").
#[cfg(not(target_os = "haiku"))]
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.
#[cfg(not(target_os = "haiku"))]
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.
#[cfg(not(target_os = "haiku"))]
LOG_NDELAY;
/// Write the message to standard error output as well to the system log.
#[cfg(not(target_os = "haiku"))]
LOG_PERROR;
}
}

libc_enum! {
/// Severity levels for log messages.
#[repr(i32)]
#[non_exhaustive]
pub enum Severity {
tisonkun marked this conversation as resolved.
Show resolved Hide resolved
/// 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
15 changes: 15 additions & 0 deletions test/test_syslog.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
use nix::syslog::{openlog, syslog, Facility, LogFlags, Severity};

#[test]
fn test_syslog_hello_world() {
#[cfg(not(target_os = "haiku"))]
let flags = LogFlags::LOG_PID;
#[cfg(target_os = "haiku")]
let flags = LogFlags::empty();

openlog(None::<&str>, flags, Facility::LOG_USER).unwrap();
syslog(Severity::LOG_EMERG, "Hello, nix!").unwrap();

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