• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

NVIDIA / nvrc / 20318372647

17 Dec 2025 09:41PM UTC coverage: 33.129% (-3.3%) from 36.443%
20318372647

Pull #81

github

web-flow
Merge 43e2c26ed into 760d90f29
Pull Request #81: General nvrc fixes

2 of 32 new or added lines in 3 files covered. (6.25%)

31 existing lines in 4 files now uncovered.

108 of 326 relevant lines covered (33.13%)

0.52 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

62.5
/src/syslog.rs
1
// SPDX-License-Identifier: Apache-2.0
2
// Copyright (c) NVIDIA CORPORATION
3

4
use log::{debug, error, info, warn};
5
use std::fs;
6
use std::os::fd::AsFd;
7
use std::os::unix::net::UnixDatagram;
8
use std::path::Path;
9
use std::sync::OnceLock;
10

11
use nix::poll::{PollFd, PollFlags};
12

13
static SYSLOG: OnceLock<UnixDatagram> = OnceLock::new();
14

15
/// Initialize the global syslog socket at /dev/log
NEW
16
pub fn init() -> std::io::Result<()> {
×
17
    // Use get_or_init with immediate setup since dev_log_setup is infallible for init purposes
NEW
18
    if SYSLOG.get().is_none() {
×
NEW
19
        let socket = dev_log_setup()?;
×
NEW
20
        let _ = SYSLOG.set(socket); // Ignore if already set (race condition)
×
21
    }
NEW
22
    Ok(())
×
23
}
24

25
/// Poll the global syslog socket for messages
NEW
26
pub fn poll() -> std::io::Result<()> {
×
NEW
27
    if let Some(sock) = SYSLOG.get() {
×
NEW
28
        poll_dev_log(sock)?;
×
29
    }
NEW
30
    Ok(())
×
31
}
32

33
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
34
#[repr(u8)]
35
enum SyslogSeverity {
36
    Emergency = 0, // System is unusable
37
    Alert,         // Action must be taken immediately
38
    Critical,      // Critical conditions
39
    Error,         // Error conditions
40
    Warning,       // Warning conditions
41
    Notice,        // Normal but significant condition
42
    Info,          // Informational messages
43
    Debug,         // Debug-level messages
44
}
45

46
impl From<u8> for SyslogSeverity {
47
    fn from(v: u8) -> Self {
3✔
48
        match v & 0x07 {
3✔
49
            0 => Self::Emergency,
1✔
50
            1 => Self::Alert,
1✔
51
            2 => Self::Critical,
1✔
52
            3 => Self::Error,
1✔
53
            4 => Self::Warning,
1✔
54
            5 => Self::Notice,
1✔
55
            6 => Self::Info,
1✔
56
            _ => Self::Debug,
1✔
57
        }
58
    }
59
}
60

61
impl SyslogSeverity {
62
    fn log(self, msg: &str) {
1✔
63
        match self {
1✔
64
            Self::Emergency | Self::Alert | Self::Critical | Self::Error => error!("{}", msg),
×
65
            Self::Warning => warn!("{}", msg),
×
66
            Self::Notice | Self::Info => info!("{}", msg),
2✔
67
            Self::Debug => debug!("{}", msg),
×
68
        }
69
    }
70
}
71

72
#[derive(Debug)]
73
struct SyslogMessage {
74
    severity: SyslogSeverity,
75
    content: String,
76
}
77

78
impl SyslogMessage {
79
    fn parse(raw: &str) -> Self {
1✔
80
        let r = raw.trim_end();
2✔
81

82
        if r.starts_with('<') {
3✔
83
            if let Some(end) = r.find('>') {
2✔
84
                if let Ok(p) = r[1..end].parse::<u8>() {
3✔
85
                    return Self {
3✔
86
                        severity: SyslogSeverity::from(p),
3✔
87
                        content: r[end + 1..].to_owned(),
3✔
88
                    };
89
                }
90
            }
91
        }
92

93
        Self {
94
            severity: SyslogSeverity::Info,
95
            content: format!("syslog: {}", r),
1✔
96
        }
97
    }
98

99
    fn log(&self) {
1✔
100
        self.severity.log(&self.content);
1✔
101
    }
102
}
103

104
pub fn dev_log_setup() -> std::io::Result<UnixDatagram> {
×
105
    let p = Path::new("/dev/log");
×
106

107
    if p.exists() {
×
108
        fs::remove_file(p)?;
×
109
    }
110

111
    UnixDatagram::bind("/dev/log")
×
112
}
113

114
pub fn poll_dev_log(sock: &UnixDatagram) -> std::io::Result<()> {
1✔
115
    let mut fds = [PollFd::new(sock.as_fd(), PollFlags::POLLIN)];
1✔
116

117
    // Non-blocking poll - return immediately if no data
118
    let poll_count = nix::poll::poll(&mut fds, nix::poll::PollTimeout::ZERO)
2✔
119
        .map_err(|e| std::io::Error::other(e.to_string()))?;
1✔
120

121
    if poll_count == 0 {
1✔
122
        return Ok(()); // No data available
1✔
123
    }
124

125
    if let Some(re) = fds[0].revents() {
×
126
        if re.contains(PollFlags::POLLIN) {
×
127
            forward_syslog_message(sock)?;
×
128
        }
129
    }
130

131
    Ok(())
×
132
}
133

134
fn forward_syslog_message(sock: &UnixDatagram) -> std::io::Result<()> {
1✔
135
    let mut buf = [0u8; 4096];
1✔
136
    let (len, _) = sock.recv_from(&mut buf)?;
1✔
137

138
    SyslogMessage::parse(&String::from_utf8_lossy(&buf[..len])).log();
1✔
139

140
    Ok(())
1✔
141
}
142

143
#[cfg(test)]
144
mod tests {
145
    use super::*;
146
    use nix::unistd::Uid;
147
    use serial_test::serial;
148
    use std::env;
149
    use std::os::unix::net::UnixDatagram;
150
    use std::process::Command;
151
    use tempfile::TempDir;
152

153
    fn rerun_with_sudo() {
154
        let args: Vec<String> = env::args().collect();
155
        let out = Command::new("sudo").args(&args).status();
156

157
        if let Ok(o) = out {
158
            if !o.success() {
159
                panic!("not running with sudo")
160
            }
161
        }
162
    }
163

164
    #[test]
165
    fn test_syslog_priority_parsing() {
166
        let p = UnixDatagram::pair().unwrap();
167
        p.1.send(b"<0>Emergency message").unwrap();
168

169
        let mut b = [0u8; 4096];
170
        let (l, _) = p.0.recv_from(&mut b).unwrap();
171
        let msg = String::from_utf8_lossy(&b[..l]);
172

173
        assert!(msg.starts_with('<'));
174
        assert_eq!(&msg[1..2], "0");
175
    }
176

177
    #[test]
178
    fn test_syslog_priority_extraction() {
179
        for (i, e) in [
180
            ("<0>Emergency", 0u8),
181
            ("<3>Error", 3u8),
182
            ("<6>Info", 6u8),
183
            ("<7>Debug", 7u8),
184
        ] {
185
            if let Some(end) = i.find('>') {
186
                if let Ok(p) = i[1..end].parse::<u8>() {
187
                    assert_eq!(p & 0x07, e);
188
                }
189
            }
190
        }
191
    }
192

193
    #[test]
194
    fn test_syslog_invalid_priority() {
195
        for i in [
196
            "<abc>Invalid priority",
197
            "<>Empty priority",
198
            "<256>Too large priority",
199
            "No priority at all",
200
            "<6 Missing closing bracket",
201
        ] {
202
            if i.starts_with('<') {
203
                if let Some(end) = i.find('>') {
204
                    let _ = i[1..end].parse::<u8>();
205
                }
206
            }
207
        }
208
    }
209

210
    #[test]
211
    fn test_poll_dev_log_no_data() {
212
        let p = UnixDatagram::pair().unwrap();
213

214
        // Poll with no data should return Ok(()) without blocking
215
        let result = poll_dev_log(&p.0);
216
        assert!(result.is_ok());
217
    }
218

219
    #[test]
220
    #[serial]
221
    fn test_dev_log_setup_permissions() {
222
        if !Uid::effective().is_root() {
223
            return rerun_with_sudo();
224
        }
225

226
        let td = TempDir::new().unwrap();
227
        let path = td.path().join("log");
228

229
        std::fs::File::create(&path).unwrap();
230
        assert!(path.exists());
231

232
        std::fs::remove_file(&path).unwrap();
233
        assert!(!path.exists());
234
    }
235

236
    #[test]
237
    fn test_syslog_message_format_with_priority() {
238
        for (i, e) in [
239
            (
240
                "<0>Emergency: System unusable",
241
                "Emergency: System unusable",
242
            ),
243
            ("<3>Error: Something failed", "Error: Something failed"),
244
            ("<6>Info: Normal operation", "Info: Normal operation"),
245
            ("<7>Debug: Detailed info", "Debug: Detailed info"),
246
        ] {
247
            if let Some(end) = i.find('>') {
248
                assert_eq!(&i[end + 1..], e);
249
            }
250
        }
251
    }
252

253
    #[test]
254
    fn test_syslog_facility_and_severity() {
255
        for (p, f, s) in [(0, 0, 0), (16, 2, 0), (24, 3, 0), (22, 2, 6), (30, 3, 6)] {
256
            assert_eq!(p >> 3, f);
257
            assert_eq!(p & 0x07, s);
258
        }
259
    }
260

261
    #[test]
262
    #[serial]
263
    fn test_forward_syslog_message_integration() {
264
        let p = UnixDatagram::pair().unwrap();
265
        p.1.send(b"<6>Test info message from syslog").unwrap();
266

267
        assert!(forward_syslog_message(&p.0).is_ok());
268
    }
269

270
    #[test]
271
    fn test_syslog_message_parsing() {
272
        let m = SyslogMessage::parse("<3>Error message");
273
        assert_eq!(m.severity as u8, 3);
274
        assert_eq!(m.content, "Error message");
275

276
        let m = SyslogMessage::parse("No priority message");
277
        assert_eq!(m.severity as u8, 6);
278
        assert_eq!(m.content, "syslog: No priority message");
279

280
        let m = SyslogMessage::parse("<abc>Invalid priority");
281
        assert_eq!(m.severity as u8, 6);
282
        assert_eq!(m.content, "syslog: <abc>Invalid priority");
283
    }
284

285
    #[test]
286
    fn test_syslog_severity_conversion() {
287
        assert_eq!(SyslogSeverity::from(0), SyslogSeverity::Emergency);
288
        assert_eq!(SyslogSeverity::from(1), SyslogSeverity::Alert);
289
        assert_eq!(SyslogSeverity::from(2), SyslogSeverity::Critical);
290
        assert_eq!(SyslogSeverity::from(3), SyslogSeverity::Error);
291
        assert_eq!(SyslogSeverity::from(4), SyslogSeverity::Warning);
292
        assert_eq!(SyslogSeverity::from(5), SyslogSeverity::Notice);
293
        assert_eq!(SyslogSeverity::from(6), SyslogSeverity::Info);
294
        assert_eq!(SyslogSeverity::from(7), SyslogSeverity::Debug);
295

296
        assert_eq!(SyslogSeverity::from(24), SyslogSeverity::Emergency);
297
        assert_eq!(SyslogSeverity::from(22), SyslogSeverity::Info);
298
    }
299

300
    #[test]
301
    fn test_syslog_message_edge_cases() {
302
        let m = SyslogMessage::parse("");
303
        assert_eq!(m.severity as u8, 6);
304
        assert_eq!(m.content, "syslog: ");
305

306
        let m = SyslogMessage::parse("<>");
307
        assert_eq!(m.severity as u8, 6);
308
        assert_eq!(m.content, "syslog: <>");
309

310
        let m = SyslogMessage::parse("<5 Missing closing");
311
        assert_eq!(m.severity as u8, 6);
312
        assert_eq!(m.content, "syslog: <5 Missing closing");
313

314
        let m = SyslogMessage::parse("<4>");
315
        assert_eq!(m.severity as u8, 4);
316
        assert_eq!(m.content, "");
317
    }
318
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc