Files
RustPython/crates/stdlib/src/syslog.rs
T
youknowdot 1052e613ed Preserve interpreter-boundary diagnostics and syslog restrictions (#9365)
* Preserve Unicode in thread unraisable messages

Keep the callable representation as WTF-8 for unraisable hooks and audit callbacks. Preserve the existing String entry point and stderr fallback; this does not claim lossless stderr rendering.

Assisted-by: OpenAI Codex:model unavailable

* Use the WTF-8 string conversion constructor

Use the existing From<&str> implementation to copy the message prefix without changing Unicode semantics.

Assisted-by: OpenAI Codex:model unavailable

* Preserve cross-interpreter marshal error causes

Wrap failures from existing code/function serialization helpers as NotShareableError, matching the CPython 3.15 marshal boundary while retaining the original exception cause. No new test C API or lifecycle behavior.

Assisted-by: OpenAI Codex:model-unavailable

* Enforce syslog interpreter boundaries and sync queue cleanup

Reject subinterpreter openlog/closelog before audit or host mutation, and
reject implicit syslog opening after audit when the process logger is not
already open, matching CPython 3.15 guard placement and error text.
Already-open logging and setlogmask remain unchanged.

Migrate concurrent/interpreters/_queues.py through scripts/update_lib to
CPython d220409a13b77de7a6c30abe16ea90db01528edc, removing unused eager
pickle import and exception targets. Native serialization imports pickle
on demand; module import timing and debug locals may nevertheless differ.

No new fixtures or interpreter lifecycle mechanisms are introduced.

Assisted-by: OpenAI Codex:model version unavailable
2026-10-26 14:15:38 +09:00

171 lines
5.2 KiB
Rust

// spell-checker:ignore logoption openlog setlogmask upto NDELAY ODELAY
pub(crate) use syslog::module_def;
#[pymodule(name = "syslog")]
mod syslog {
use crate::vm::{
Py, PyObjectRef, PyPayload, PyResult, VirtualMachine,
builtins::{PyStr, PyStrRef},
convert::ToPyException,
utils::ToCString,
};
use rustpython_host_env::syslog as host_syslog;
#[pyattr]
use host_syslog::{
LOG_ALERT, LOG_AUTH, LOG_CONS, LOG_CRIT, LOG_DAEMON, LOG_DEBUG, LOG_EMERG, LOG_ERR,
LOG_INFO, LOG_KERN, LOG_LOCAL0, LOG_LOCAL1, LOG_LOCAL2, LOG_LOCAL3, LOG_LOCAL4, LOG_LOCAL5,
LOG_LOCAL6, LOG_LOCAL7, LOG_LPR, LOG_MAIL, LOG_NDELAY, LOG_NEWS, LOG_NOTICE, LOG_NOWAIT,
LOG_ODELAY, LOG_PID, LOG_SYSLOG, LOG_USER, LOG_UUCP, LOG_WARNING,
};
#[cfg(not(target_os = "redox"))]
#[pyattr]
use host_syslog::{LOG_AUTHPRIV, LOG_CRON, LOG_PERROR};
#[cfg(target_vendor = "apple")]
#[pyattr]
use host_syslog::{LOG_FTP, LOG_INSTALL, LOG_LAUNCHD, LOG_NETINFO, LOG_RAS, LOG_REMOTEAUTH};
fn ident_to_utf8_cstring(
ident: &Py<PyStr>,
vm: &VirtualMachine,
) -> PyResult<alloc::ffi::CString> {
let utf8 = ident.to_str().ok_or_else(|| {
let start = ident
.as_wtf8()
.code_points()
.position(|c| c.to_char().is_none())
.unwrap_or(0);
vm.new_unicode_encode_error(
vm.ctx.new_str("utf-8"),
ident.to_owned(),
start,
start + 1,
vm.ctx.new_str("surrogates not allowed"),
)
})?;
alloc::ffi::CString::new(utf8).map_err(|err| err.to_pyexception(vm))
}
fn get_argv(vm: &VirtualMachine) -> Option<PyStrRef> {
if let Some(argv) = vm.state.config.settings.argv.first()
&& !argv.is_empty()
{
return Some(
PyStr::from(match argv.find('\\') {
Some(value) => &argv[value..],
None => argv,
})
.into_ref(&vm.ctx),
);
}
None
}
#[derive(Default, FromArgs)]
struct OpenLogArgs {
#[pyarg(any, optional)]
ident: Option<PyStrRef>,
#[pyarg(any, default)]
logoption: i32,
#[pyarg(any, default = ::LOG_USER)]
facility: i32,
}
#[pyfunction]
fn openlog(args: OpenLogArgs, vm: &VirtualMachine) -> PyResult<()> {
if !vm.state.is_main_interpreter() {
return Err(vm.new_runtime_error("subinterpreter can't use syslog.openlog()"));
}
let logoption = args.logoption;
let facility = args.facility;
let ident = match args.ident.clone() {
Some(ident) => Some(ident_to_utf8_cstring(&ident, vm)?),
None => get_argv(vm)
.map(|argv| ident_to_utf8_cstring(&argv, vm))
.transpose()?,
}
.map(|ident| ident.into_boxed_c_str());
vm.audit("syslog.openlog", || {
let audit_ident: PyObjectRef = args.ident.map_or_else(
|| get_argv(vm).map_or_else(|| vm.ctx.none(), Into::into),
Into::into,
);
(audit_ident, logoption, facility)
})?;
host_syslog::openlog(ident, logoption, facility);
Ok(())
}
#[derive(FromArgs)]
struct SysLogArgs {
#[pyarg(positional)]
priority: PyObjectRef,
#[pyarg(positional, optional)]
message: Option<PyStrRef>,
}
#[pyfunction]
fn syslog(args: SysLogArgs, vm: &VirtualMachine) -> PyResult<()> {
let (priority, msg) = match args.message {
Some(s) => (args.priority.try_into_value(vm)?, s),
None => (LOG_INFO, args.priority.try_into_value(vm)?),
};
vm.audit("syslog.syslog", || (priority, msg.clone()))?;
if !host_syslog::is_open() {
if !vm.state.is_main_interpreter() {
return Err(vm.new_runtime_error(
"subinterpreter can't use syslog.syslog() until the syslog is opened by the main interpreter",
));
}
openlog(
OpenLogArgs {
facility: LOG_USER,
..OpenLogArgs::default()
},
vm,
)?;
}
let cmsg = msg.to_cstring(vm)?;
host_syslog::syslog(priority, cmsg.as_c_str());
Ok(())
}
#[pyfunction]
fn closelog(vm: &VirtualMachine) -> PyResult<()> {
if !vm.state.is_main_interpreter() {
return Err(vm.new_runtime_error("subinterpreter can't use syslog.closelog()"));
}
vm.audit("syslog.closelog", || ())?;
host_syslog::closelog();
Ok(())
}
#[pyfunction]
fn setlogmask(maskpri: i32, vm: &VirtualMachine) -> PyResult<i32> {
vm.audit("syslog.setlogmask", || (maskpri,))?;
Ok(host_syslog::setlogmask(maskpri))
}
#[inline]
#[pyfunction(name = "LOG_MASK")]
const fn log_mask(pri: i32) -> i32 {
host_syslog::log_mask(pri)
}
#[inline]
#[pyfunction(name = "LOG_UPTO")]
const fn log_upto(pri: i32) -> i32 {
host_syslog::log_upto(pri)
}
}