1use std::fs;
39use std::io;
40use std::path::Path;
41
42use crate::error::{Result, SaferRingError};
43
44#[derive(Debug, Clone, Copy, PartialEq, Eq)]
46pub enum Backend {
47 IoUring,
49 Epoll,
51 Stub,
53}
54
55impl Backend {
56 pub fn description(&self) -> &'static str {
58 match self {
59 Backend::IoUring => "High-performance io_uring backend",
60 Backend::Epoll => "Traditional epoll-based backend",
61 Backend::Stub => "Stub implementation for non-Linux platforms",
62 }
63 }
64
65 pub fn supports_advanced_features(&self) -> bool {
67 matches!(self, Backend::IoUring)
68 }
69
70 pub fn performance_multiplier(&self) -> f32 {
72 match self {
73 Backend::IoUring => 3.0, Backend::Epoll => 1.0, Backend::Stub => 0.1, }
77 }
78}
79
80#[derive(Debug)]
85pub struct Runtime {
86 backend: Backend,
87 environment: EnvironmentInfo,
88}
89
90impl Runtime {
91 pub fn auto_detect() -> Result<Self> {
113 let environment = EnvironmentInfo::detect();
114 let backend = Self::select_best_backend(&environment)?;
115
116 if environment.is_cloud_environment() && backend != Backend::IoUring {
118 eprintln!("WARNING: io_uring not available in cloud environment");
119 eprintln!("Performance may be degraded. See documentation for configuration tips:");
120 eprintln!("- Docker: Add --cap-add SYS_ADMIN or use --privileged");
121 eprintln!("- Kubernetes: Set privileged: true or configure seccomp/apparmor");
122 eprintln!("- Cloud Run/Lambda: Consider using Cloud Functions with custom runtimes");
123 }
124
125 Ok(Self {
126 backend,
127 environment,
128 })
129 }
130
131 pub fn with_backend(backend: Backend) -> Result<Self> {
152 let environment = EnvironmentInfo::detect();
153
154 match backend {
156 Backend::IoUring => {
157 if !Self::is_io_uring_available() {
158 return Err(SaferRingError::Io(io::Error::new(
159 io::ErrorKind::Unsupported,
160 "io_uring backend requested but not available on this system",
161 )));
162 }
163 }
164 Backend::Epoll => {
165 #[cfg(not(target_os = "linux"))]
166 {
167 return Err(SaferRingError::Io(io::Error::new(
168 io::ErrorKind::Unsupported,
169 "epoll backend only available on Linux",
170 )));
171 }
172 }
173 Backend::Stub => {
174 }
176 }
177
178 Ok(Self {
179 backend,
180 environment,
181 })
182 }
183
184 pub fn backend(&self) -> Backend {
186 self.backend
187 }
188
189 pub fn environment(&self) -> &EnvironmentInfo {
191 &self.environment
192 }
193
194 pub fn is_cloud_environment(&self) -> bool {
196 self.environment.is_cloud_environment()
197 }
198
199 pub fn performance_guidance(&self) -> Vec<&'static str> {
201 let mut guidance = Vec::new();
202
203 match self.backend {
204 Backend::IoUring => {
205 guidance.push("✓ Using high-performance io_uring backend");
206 if self.environment.container_runtime.is_some() {
207 guidance
208 .push("Consider tuning container security settings for better performance");
209 }
210 }
211 Backend::Epoll => {
212 guidance.push("⚠ Using epoll fallback - performance may be limited");
213 if self.is_cloud_environment() {
214 guidance
215 .push("Check cloud platform documentation for enabling io_uring support");
216 }
217 }
218 Backend::Stub => {
219 guidance.push("⚠ Using stub implementation - limited functionality");
220 guidance.push("Consider running on Linux for better performance");
221 }
222 }
223
224 if let Some(container) = &self.environment.container_runtime {
225 match container.as_str() {
226 "docker" => {
227 guidance.push("Docker detected: Add --cap-add SYS_ADMIN for io_uring support");
228 }
229 "containerd" | "cri-o" => {
230 guidance.push(
231 "Kubernetes detected: Configure privileged pods or custom seccomp profiles",
232 );
233 }
234 _ => {}
235 }
236 }
237
238 guidance
239 }
240
241 fn select_best_backend(_environment: &EnvironmentInfo) -> Result<Backend> {
243 #[cfg(not(target_os = "linux"))]
245 {
246 Ok(Backend::Stub)
247 }
248
249 #[cfg(target_os = "linux")]
250 {
251 if Self::is_io_uring_available() {
253 if _environment.is_cloud_environment() {
255 if let Some(restriction) = Self::check_io_uring_restrictions() {
257 eprintln!("io_uring restricted: {restriction}");
258 return Ok(Backend::Epoll);
259 }
260 }
261 return Ok(Backend::IoUring);
262 }
263
264 Ok(Backend::Epoll)
266 }
267 }
268
269 #[cfg(target_os = "linux")]
271 fn is_io_uring_available() -> bool {
272 io_uring::IoUring::new(1).is_ok()
274 }
275
276 #[cfg(not(target_os = "linux"))]
278 fn is_io_uring_available() -> bool {
279 false
280 }
281
282 #[cfg(target_os = "linux")]
284 #[allow(dead_code)]
285 fn check_io_uring_restrictions() -> Option<String> {
286 if let Ok(status) = fs::read_to_string("/proc/self/status") {
288 if status.contains("Seccomp:") && !status.contains("Seccomp:\t0") {
289 return Some("seccomp profile may restrict io_uring system calls".to_string());
290 }
291 }
292
293 if Path::new("/proc/self/attr/current").exists() {
295 if let Ok(apparmor) = fs::read_to_string("/proc/self/attr/current") {
296 if !apparmor.trim().is_empty() && apparmor.trim() != "unconfined" {
297 return Some("AppArmor profile may restrict io_uring".to_string());
298 }
299 }
300 }
301
302 if Path::new("/.dockerenv").exists() {
304 return Some("Docker environment detected - io_uring may be restricted".to_string());
305 }
306
307 None
308 }
309
310 #[cfg(not(target_os = "linux"))]
312 #[allow(dead_code)]
313 fn check_io_uring_restrictions() -> Option<String> {
314 None
315 }
316}
317
318#[derive(Debug)]
323pub struct EnvironmentInfo {
324 pub container_runtime: Option<String>,
326 pub kubernetes: bool,
328 pub serverless: bool,
330 pub kernel_version: Option<String>,
332 pub cpu_count: usize,
334}
335
336impl EnvironmentInfo {
337 pub fn detect() -> Self {
339 Self {
340 container_runtime: Self::detect_container_runtime(),
341 kubernetes: Self::detect_kubernetes(),
342 serverless: Self::detect_serverless(),
343 kernel_version: Self::detect_kernel_version(),
344 cpu_count: Self::detect_cpu_count(),
345 }
346 }
347
348 pub fn is_cloud_environment(&self) -> bool {
350 self.container_runtime.is_some() || self.kubernetes || self.serverless
351 }
352
353 fn detect_container_runtime() -> Option<String> {
355 if Path::new("/.dockerenv").exists() {
357 return Some("docker".to_string());
358 }
359
360 if let Ok(cgroup) = fs::read_to_string("/proc/1/cgroup") {
362 if cgroup.contains("docker") {
363 return Some("docker".to_string());
364 }
365 if cgroup.contains("containerd") {
366 return Some("containerd".to_string());
367 }
368 if cgroup.contains("cri-o") {
369 return Some("cri-o".to_string());
370 }
371 }
372
373 None
374 }
375
376 fn detect_kubernetes() -> bool {
378 std::env::var("KUBERNETES_SERVICE_HOST").is_ok()
379 || Path::new("/var/run/secrets/kubernetes.io").exists()
380 }
381
382 fn detect_serverless() -> bool {
384 std::env::var("AWS_LAMBDA_FUNCTION_NAME").is_ok() ||
386 std::env::var("FUNCTION_NAME").is_ok() ||
388 std::env::var("AZURE_FUNCTIONS_ENVIRONMENT").is_ok() ||
390 std::env::var("K_SERVICE").is_ok()
392 }
393
394 #[cfg(target_os = "linux")]
396 fn detect_kernel_version() -> Option<String> {
397 fs::read_to_string("/proc/version")
398 .ok()
399 .and_then(|v| v.split_whitespace().nth(2).map(|s| s.to_string()))
400 }
401
402 #[cfg(not(target_os = "linux"))]
404 fn detect_kernel_version() -> Option<String> {
405 None
406 }
407
408 fn detect_cpu_count() -> usize {
410 std::thread::available_parallelism()
411 .map(|p| p.get())
412 .unwrap_or(1)
413 }
414}
415
416pub fn is_io_uring_available() -> bool {
432 Runtime::is_io_uring_available()
433}
434
435pub fn get_environment_info() -> EnvironmentInfo {
449 EnvironmentInfo::detect()
450}
451
452#[cfg(test)]
453mod tests {
454 use super::*;
455
456 #[test]
457 fn test_backend_properties() {
458 assert_eq!(
459 Backend::IoUring.description(),
460 "High-performance io_uring backend"
461 );
462 assert_eq!(
463 Backend::Epoll.description(),
464 "Traditional epoll-based backend"
465 );
466 assert_eq!(
467 Backend::Stub.description(),
468 "Stub implementation for non-Linux platforms"
469 );
470
471 assert!(Backend::IoUring.supports_advanced_features());
472 assert!(!Backend::Epoll.supports_advanced_features());
473 assert!(!Backend::Stub.supports_advanced_features());
474
475 assert_eq!(Backend::IoUring.performance_multiplier(), 3.0);
476 assert_eq!(Backend::Epoll.performance_multiplier(), 1.0);
477 assert_eq!(Backend::Stub.performance_multiplier(), 0.1);
478 }
479
480 #[test]
481 fn test_runtime_auto_detect() {
482 let runtime = Runtime::auto_detect().unwrap();
483
484 match runtime.backend() {
486 Backend::IoUring | Backend::Epoll | Backend::Stub => {}
487 }
488
489 let env = runtime.environment();
491 assert!(env.cpu_count > 0);
492 }
493
494 #[test]
495 fn test_environment_detection() {
496 let env = EnvironmentInfo::detect();
497
498 assert!(env.cpu_count > 0);
500
501 let _ = env.is_cloud_environment();
503 }
504
505 #[test]
506 fn test_performance_guidance() {
507 let runtime = Runtime::auto_detect().unwrap();
508 let guidance = runtime.performance_guidance();
509
510 assert!(!guidance.is_empty());
512
513 for guide in guidance {
515 assert!(!guide.is_empty());
516 }
517 }
518
519 #[cfg(target_os = "linux")]
520 #[test]
521 fn test_stub_backend_on_non_linux_request() {
522 let runtime = Runtime::with_backend(Backend::Stub).unwrap();
524 assert_eq!(runtime.backend(), Backend::Stub);
525 }
526
527 #[cfg(not(target_os = "linux"))]
528 #[test]
529 fn test_epoll_backend_fails_on_non_linux() {
530 let result = Runtime::with_backend(Backend::Epoll);
532 assert!(result.is_err());
533 }
534
535 #[test]
536 fn test_is_io_uring_available() {
537 let _ = is_io_uring_available();
539 }
540
541 #[test]
542 fn test_get_environment_info() {
543 let env = get_environment_info();
544 assert!(env.cpu_count > 0);
545 }
546}