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

NVIDIA / nvrc / 20375622957

19 Dec 2025 04:10PM UTC coverage: 80.415% (+51.8%) from 28.618%
20375622957

push

github

web-flow
Merge pull request #84 from zvonkok/coreutils-100percent

NVRC complete code coverage

69 of 83 new or added lines in 11 files covered. (83.13%)

3 existing lines in 3 files now uncovered.

271 of 337 relevant lines covered (80.42%)

1.62 hits per line

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

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

4
//! Minimal syslog sink for ephemeral init environments.
5
//!
6
//! Programs expect /dev/log to exist for logging. As a minimal init we provide
7
//! this socket and forward messages to the kernel log via trace!(). Severity
8
//! levels are stripped since all messages go to the same destination anyway.
9

10
use log::trace;
11
use nix::poll::{PollFd, PollFlags, PollTimeout};
12
use once_cell::sync::OnceCell;
13
use std::os::fd::AsFd;
14
use std::os::unix::net::UnixDatagram;
15
use std::path::Path;
16

17
/// Global syslog socket—lazily initialized on first poll().
18
/// OnceCell ensures thread-safe one-time init. Ephemeral init runs once,
19
/// no need for reset capability.
20
static SYSLOG: OnceCell<UnixDatagram> = OnceCell::new();
21

22
const DEV_LOG: &str = "/dev/log";
23

24
/// Create and bind a Unix datagram socket at the given path.
25
fn bind(path: &Path) -> std::io::Result<UnixDatagram> {
1✔
26
    UnixDatagram::bind(path)
1✔
27
}
28

29
/// Check socket for pending messages (non-blocking).
30
/// Returns None if no data available, Some(msg) if a message was read.
31
fn poll_socket(sock: &UnixDatagram) -> std::io::Result<Option<String>> {
2✔
32
    let mut fds = [PollFd::new(sock.as_fd(), PollFlags::POLLIN)];
2✔
33
    // Non-blocking poll—init loop calls this frequently, can't afford to block
34
    let count = nix::poll::poll(&mut fds, PollTimeout::ZERO)
5✔
35
        .map_err(|e| std::io::Error::other(e.to_string()))?;
3✔
36

37
    if count == 0 {
3✔
38
        return Ok(None); // No events, no data waiting
2✔
39
    }
40

41
    let Some(revents) = fds[0].revents() else {
2✔
NEW
42
        return Ok(None); // Shouldn't happen, but handle gracefully
×
43
    };
44

45
    if !revents.contains(PollFlags::POLLIN) {
1✔
NEW
46
        return Ok(None); // Event wasn't POLLIN (e.g., error flag)
×
47
    }
48

49
    // Read the message—4KB buffer matches typical syslog max message size
50
    let mut buf = [0u8; 4096];
1✔
51
    let (len, _) = sock.recv_from(&mut buf)?;
2✔
52
    let msg = String::from_utf8_lossy(&buf[..len]);
3✔
53
    Ok(Some(strip_priority(msg.trim_end()).to_string()))
6✔
54
}
55

56
/// Poll the global /dev/log socket, logging any message via trace!().
57
/// Lazily initializes /dev/log on first call.
58
/// Drains one message per call—rate-limited to prevent DoS by syslog flooding.
59
/// Caller loops at ~2 msg/sec (500ms sleep between calls).
60
pub fn poll() -> std::io::Result<()> {
2✔
61
    poll_at(Path::new(DEV_LOG))
2✔
62
}
63

64
/// Internal: poll a specific socket path (for unit tests).
65
/// Production code uses poll() which hardcodes /dev/log.
66
fn poll_at(path: &Path) -> std::io::Result<()> {
2✔
67
    let sock: &UnixDatagram = if path == Path::new(DEV_LOG) {
1✔
68
        SYSLOG.get_or_try_init(|| bind(path))?
4✔
69
    } else {
70
        // For testing: create a one-shot socket (caller manages lifecycle)
71
        return poll_once(path);
1✔
72
    };
73

74
    if let Some(msg) = poll_socket(sock)? {
×
75
        trace!("{}", msg);
×
76
    }
77

78
    Ok(())
×
79
}
80

81
/// One-shot poll for testing: bind, poll once, return.
82
/// Socket is dropped after call—suitable for tests with temp paths.
83
fn poll_once(path: &Path) -> std::io::Result<()> {
1✔
84
    let sock = bind(path)?;
2✔
85
    if let Some(msg) = poll_socket(&sock)? {
4✔
NEW
86
        trace!("{}", msg);
×
87
    }
88
    Ok(())
2✔
89
}
90

91
/// Strip the syslog priority prefix <N> from a message.
92
/// Priority levels are noise for us—all messages go to trace!() equally.
93
/// Example: "<6>hello" → "hello"
94
fn strip_priority(msg: &str) -> &str {
3✔
95
    msg.strip_prefix('<')
3✔
96
        .and_then(|s| s.find('>').map(|i| &s[i + 1..]))
15✔
97
        .unwrap_or(msg)
3✔
98
}
99

100
#[cfg(test)]
101
mod tests {
102
    use super::*;
103
    use tempfile::TempDir;
104

105
    // === strip_priority tests ===
106

107
    #[test]
108
    fn test_strip_priority_normal() {
109
        assert_eq!(strip_priority("<6>test message"), "test message");
110
        assert_eq!(strip_priority("<13>another msg"), "another msg");
111
        assert_eq!(strip_priority("<191>high pri"), "high pri");
112
    }
113

114
    #[test]
115
    fn test_strip_priority_no_prefix() {
116
        assert_eq!(strip_priority("no prefix"), "no prefix");
117
    }
118

119
    #[test]
120
    fn test_strip_priority_edge_cases() {
121
        assert_eq!(strip_priority("<>empty"), "empty");
122
        assert_eq!(strip_priority("<6>"), "");
123
        assert_eq!(strip_priority(""), "");
124
        assert_eq!(strip_priority("<"), "<");
125
        assert_eq!(strip_priority("<6"), "<6"); // No closing >
126
    }
127

128
    // === bind tests ===
129

130
    #[test]
131
    fn test_bind_success() {
132
        let tmp = TempDir::new().unwrap();
133
        let path = tmp.path().join("test.sock");
134
        let sock = bind(&path);
135
        assert!(sock.is_ok());
136
    }
137

138
    #[test]
139
    fn test_bind_nonexistent_dir() {
140
        let path = Path::new("/nonexistent/dir/test.sock");
141
        let err = bind(path).unwrap_err();
142
        // Should fail with "No such file or directory" (ENOENT)
143
        assert_eq!(err.kind(), std::io::ErrorKind::NotFound);
144
    }
145

146
    #[test]
147
    fn test_bind_already_exists() {
148
        let tmp = TempDir::new().unwrap();
149
        let path = tmp.path().join("test.sock");
150
        let _sock1 = bind(&path).unwrap();
151
        // Binding again to same path should fail with "Address already in use"
152
        let err = bind(&path).unwrap_err();
153
        assert_eq!(err.kind(), std::io::ErrorKind::AddrInUse);
154
    }
155

156
    // === poll_socket tests ===
157

158
    #[test]
159
    fn test_poll_socket_no_data() {
160
        let tmp = TempDir::new().unwrap();
161
        let path = tmp.path().join("test.sock");
162
        let sock = bind(&path).unwrap();
163

164
        let result = poll_socket(&sock).unwrap();
165
        assert_eq!(result, None);
166
    }
167

168
    #[test]
169
    fn test_poll_socket_with_data() {
170
        let tmp = TempDir::new().unwrap();
171
        let path = tmp.path().join("test.sock");
172
        let server = bind(&path).unwrap();
173

174
        let client = UnixDatagram::unbound().unwrap();
175
        client.send_to(b"<6>hello world", &path).unwrap();
176

177
        let result = poll_socket(&server).unwrap();
178
        assert_eq!(result, Some("hello world".to_string()));
179
    }
180

181
    #[test]
182
    fn test_poll_socket_strips_priority() {
183
        let tmp = TempDir::new().unwrap();
184
        let path = tmp.path().join("test.sock");
185
        let server = bind(&path).unwrap();
186

187
        let client = UnixDatagram::unbound().unwrap();
188
        client.send_to(b"<3>error message", &path).unwrap();
189

190
        let result = poll_socket(&server).unwrap();
191
        assert_eq!(result, Some("error message".to_string()));
192
    }
193

194
    #[test]
195
    fn test_poll_socket_multiple_messages() {
196
        let tmp = TempDir::new().unwrap();
197
        let path = tmp.path().join("test.sock");
198
        let server = bind(&path).unwrap();
199

200
        let client = UnixDatagram::unbound().unwrap();
201
        client.send_to(b"<6>first", &path).unwrap();
202
        client.send_to(b"<6>second", &path).unwrap();
203

204
        // poll_socket drains one at a time
205
        let result1 = poll_socket(&server).unwrap();
206
        assert_eq!(result1, Some("first".to_string()));
207

208
        let result2 = poll_socket(&server).unwrap();
209
        assert_eq!(result2, Some("second".to_string()));
210

211
        // No more messages
212
        let result3 = poll_socket(&server).unwrap();
213
        assert_eq!(result3, None);
214
    }
215

216
    #[test]
217
    fn test_poll_socket_trims_trailing_whitespace() {
218
        let tmp = TempDir::new().unwrap();
219
        let path = tmp.path().join("test.sock");
220
        let server = bind(&path).unwrap();
221

222
        let client = UnixDatagram::unbound().unwrap();
223
        client.send_to(b"<6>message with newline\n", &path).unwrap();
224

225
        let result = poll_socket(&server).unwrap();
226
        assert_eq!(result, Some("message with newline".to_string()));
227
    }
228

229
    // === poll_at / poll_once tests ===
230

231
    #[test]
232
    fn test_poll_once_no_data() {
233
        let tmp = TempDir::new().unwrap();
234
        let path = tmp.path().join("test.sock");
235

236
        // poll_once will bind and poll - should succeed with no messages
237
        let result = poll_once(&path);
238
        assert!(result.is_ok());
239
    }
240

241
    #[test]
242
    fn test_poll_once_with_data() {
243
        let tmp = TempDir::new().unwrap();
244
        let path = tmp.path().join("test.sock");
245

246
        // Create server socket first
247
        let server = bind(&path).unwrap();
248

249
        // Send data
250
        let client = UnixDatagram::unbound().unwrap();
251
        client.send_to(b"<6>poll_once test", &path).unwrap();
252

253
        // poll_socket on the server
254
        let result = poll_socket(&server).unwrap();
255
        assert_eq!(result, Some("poll_once test".to_string()));
256
    }
257

258
    #[test]
259
    fn test_poll_at_custom_path() {
260
        let tmp = TempDir::new().unwrap();
261
        let path = tmp.path().join("custom.sock");
262

263
        // poll_at with non-/dev/log path uses poll_once internally
264
        let result = poll_at(&path);
265
        assert!(result.is_ok());
266
    }
267

268
    #[test]
269
    fn test_poll_dev_log() {
270
        // poll() tries to bind /dev/log - may fail if already bound or no permission
271
        // Just exercise the code path, don't assert success
272
        let _ = poll();
273
    }
274
}
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