rustpython_stdlib/
syslog.rs

1// spell-checker:ignore logoption openlog setlogmask upto
2
3pub(crate) use syslog::make_module;
4
5#[pymodule(name = "syslog")]
6mod syslog {
7    use crate::common::lock::PyRwLock;
8    use crate::vm::{
9        builtins::{PyStr, PyStrRef},
10        function::{OptionalArg, OptionalOption},
11        utils::ToCString,
12        PyObjectRef, PyPayload, PyResult, VirtualMachine,
13    };
14    use std::{ffi::CStr, os::raw::c_char};
15
16    #[pyattr]
17    use libc::{
18        LOG_ALERT, LOG_AUTH, LOG_CONS, LOG_CRIT, LOG_DAEMON, LOG_DEBUG, LOG_EMERG, LOG_ERR,
19        LOG_INFO, LOG_KERN, LOG_LOCAL0, LOG_LOCAL1, LOG_LOCAL2, LOG_LOCAL3, LOG_LOCAL4, LOG_LOCAL5,
20        LOG_LOCAL6, LOG_LOCAL7, LOG_LPR, LOG_MAIL, LOG_NDELAY, LOG_NEWS, LOG_NOTICE, LOG_NOWAIT,
21        LOG_ODELAY, LOG_PID, LOG_SYSLOG, LOG_USER, LOG_UUCP, LOG_WARNING,
22    };
23
24    #[cfg(not(target_os = "redox"))]
25    #[pyattr]
26    use libc::{LOG_AUTHPRIV, LOG_CRON, LOG_PERROR};
27
28    fn get_argv(vm: &VirtualMachine) -> Option<PyStrRef> {
29        if let Some(argv) = vm.state.settings.argv.first() {
30            if !argv.is_empty() {
31                return Some(
32                    PyStr::from(match argv.find('\\') {
33                        Some(value) => &argv[value..],
34                        None => argv,
35                    })
36                    .into_ref(&vm.ctx),
37                );
38            }
39        }
40        None
41    }
42
43    #[derive(Debug)]
44    enum GlobalIdent {
45        Explicit(Box<CStr>),
46        Implicit,
47    }
48
49    impl GlobalIdent {
50        fn as_ptr(&self) -> *const c_char {
51            match self {
52                GlobalIdent::Explicit(ref cstr) => cstr.as_ptr(),
53                GlobalIdent::Implicit => std::ptr::null(),
54            }
55        }
56    }
57
58    fn global_ident() -> &'static PyRwLock<Option<GlobalIdent>> {
59        rustpython_common::static_cell! {
60            static IDENT: PyRwLock<Option<GlobalIdent>>;
61        };
62        IDENT.get_or_init(|| PyRwLock::new(None))
63    }
64
65    #[derive(Default, FromArgs)]
66    struct OpenLogArgs {
67        #[pyarg(any, optional)]
68        ident: OptionalOption<PyStrRef>,
69        #[pyarg(any, optional)]
70        logoption: OptionalArg<i32>,
71        #[pyarg(any, optional)]
72        facility: OptionalArg<i32>,
73    }
74
75    #[pyfunction]
76    fn openlog(args: OpenLogArgs, vm: &VirtualMachine) -> PyResult<()> {
77        let logoption = args.logoption.unwrap_or(0);
78        let facility = args.facility.unwrap_or(LOG_USER);
79        let ident = match args.ident.flatten() {
80            Some(args) => Some(args.to_cstring(vm)?),
81            None => get_argv(vm).map(|argv| argv.to_cstring(vm)).transpose()?,
82        }
83        .map(|ident| ident.into_boxed_c_str());
84
85        let ident = match ident {
86            Some(ident) => GlobalIdent::Explicit(ident),
87            None => GlobalIdent::Implicit,
88        };
89
90        {
91            let mut locked_ident = global_ident().write();
92            unsafe { libc::openlog(ident.as_ptr(), logoption, facility) };
93            *locked_ident = Some(ident);
94        }
95        Ok(())
96    }
97
98    #[derive(FromArgs)]
99    struct SysLogArgs {
100        #[pyarg(positional)]
101        priority: PyObjectRef,
102        #[pyarg(positional, optional)]
103        message_object: OptionalOption<PyStrRef>,
104    }
105
106    #[pyfunction]
107    fn syslog(args: SysLogArgs, vm: &VirtualMachine) -> PyResult<()> {
108        let (priority, msg) = match args.message_object.flatten() {
109            Some(s) => (args.priority.try_into_value(vm)?, s),
110            None => (LOG_INFO, args.priority.try_into_value(vm)?),
111        };
112
113        if global_ident().read().is_none() {
114            openlog(OpenLogArgs::default(), vm)?;
115        }
116
117        let (cformat, cmsg) = ("%s".to_cstring(vm)?, msg.to_cstring(vm)?);
118        unsafe { libc::syslog(priority, cformat.as_ptr(), cmsg.as_ptr()) };
119        Ok(())
120    }
121
122    #[pyfunction]
123    fn closelog() {
124        if global_ident().read().is_some() {
125            let mut locked_ident = global_ident().write();
126            unsafe { libc::closelog() };
127            *locked_ident = None;
128        }
129    }
130
131    #[pyfunction]
132    fn setlogmask(maskpri: i32) -> i32 {
133        unsafe { libc::setlogmask(maskpri) }
134    }
135
136    #[inline]
137    #[pyfunction(name = "LOG_MASK")]
138    fn log_mask(pri: i32) -> i32 {
139        pri << 1
140    }
141
142    #[inline]
143    #[pyfunction(name = "LOG_UPTO")]
144    fn log_upto(pri: i32) -> i32 {
145        (1 << (pri + 1)) - 1
146    }
147}