Skip to content
Merged
Show file tree
Hide file tree
Changes from 20 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 = []
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 `macos`.
8 changes: 8 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,12 @@ feature! {
pub mod spawn;
}

#[cfg(unix)]
feature! {
#![feature = "syslog"]
pub mod syslog;
}

use std::ffi::{CStr, CString, OsStr};
use std::mem::MaybeUninit;
use std::os::unix::ffi::OsStrExt;
Expand Down
188 changes: 188 additions & 0 deletions src/syslog.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
//! 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

Copy link

@AlexTMjugador AlexTMjugador Jun 24, 2025

Choose a reason for hiding this comment

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

I just stumbled upon this review comment when writing a portable interface that relies on openlog and I found the different signature among different Unix flavors quite strange, and potentially inappropriate.

openlog is a function provided by the environment C library, which may be implemented in different ways even for the same Unix flavor (Linux targets routinely use both musl and glibc as C library implementations, and macOS has an history of changing relevant implementation details of this function over OS versions), so changing a function signature just because the target is Linux is something that should be done with lots of caution, as it's likely to not be proper.

Moreover, the POSIX standard says that "the application shall ensure that the string pointed to by ident remains valid during the syslog() calls that will prepend this identifier". Because we can't really know in this function at what times syslog() will be further called, the only safe lifetime for the ident parameter is 'static. And because the string is assumed to be a standard C string with no NUL characters in the middle, it should be CStr, or a str that uses the Nix path helper to check for absence of NULs (more ergonomic). Thus, I think the Linux signature of this function, potentially with a change from CStr to str + Nix path checks, is the one that should be used for all Unix flavors.

What do you think about this? Would a PR to tweak this be welcome? 😄

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};
///
/// openlog(None::<&str>, LogFlags::LOG_PID, 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>,
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.
#[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 {
/// 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,
}
}
3 changes: 3 additions & 0 deletions test/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ mod test_sendfile;
))]
mod test_spawn;

#[cfg(unix)]
mod test_syslog;

mod test_time;
mod test_unistd;

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

#[test]
fn test_syslog_hello_world() {
openlog(None::<&str>, LogFlags::LOG_PID, Facility::LOG_USER).unwrap();
syslog(Severity::LOG_EMERG, "Hello, nix!").unwrap();

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