1 //! Managing the process Routinator runs in. 2 3 use std::{fs, io}; 4 use std::future::Future; 5 use std::path::Path; 6 use std::sync::mpsc; 7 use std::sync::{Mutex, RwLock}; 8 use bytes::Bytes; 9 use chrono::{DateTime, Utc}; 10 use log::{error, LevelFilter}; 11 use tokio::runtime::Runtime; 12 use crate::config::{Config, LogTarget}; 13 use crate::error::Failed; 14 15 16 //------------ Process ------------------------------------------------------- 17 18 /// A representation of the process Routinator runs in. 19 /// 20 /// This type provides access to the configuration and the environment in a 21 /// platform independent way. 22 pub struct Process { 23 config: Config, 24 service: Option<ServiceImpl>, 25 } 26 27 impl Process { init() -> Result<(), Failed>28 pub fn init() -> Result<(), Failed> { 29 Self::init_logging()?; 30 31 Ok(()) 32 } 33 34 /// Creates a new process object. 35 /// new(config: Config) -> Self36 pub fn new(config: Config) -> Self { 37 Process { 38 service: Some(ServiceImpl::new(&config)), 39 config 40 } 41 } 42 43 /// Returns a reference to the config. config(&self) -> &Config44 pub fn config(&self) -> &Config { 45 &self.config 46 } 47 48 /// Returns an exclusive reference to the config. config_mut(&mut self) -> &mut Config49 pub fn config_mut(&mut self) -> &mut Config { 50 &mut self.config 51 } 52 } 53 54 /// # Logging 55 /// 56 impl Process { 57 /// Initialize logging. 58 /// 59 /// All diagnostic output of Routinator is done via logging, never to 60 /// stderr directly. Thus, it is important to initalize logging before 61 /// doing anything else that may result in such output. This function 62 /// does exactly that. It sets a maximum log level of `warn`, leading 63 /// only printing important information, and directs all logging to 64 /// stderr. init_logging() -> Result<(), Failed>65 fn init_logging() -> Result<(), Failed> { 66 log::set_max_level(LevelFilter::Warn); 67 if let Err(err) = log_reroute::init() { 68 eprintln!("Failed to initialize logger: {}.\nAborting.", err); 69 return Err(Failed) 70 }; 71 let dispatch = fern::Dispatch::new() 72 .level(LevelFilter::Error) 73 .chain(io::stderr()) 74 .into_log().1; 75 log_reroute::reroute_boxed(dispatch); 76 Ok(()) 77 } 78 79 /// Switches logging to the configured target. 80 /// 81 /// Once the configuration has been successfully loaded, logging should 82 /// be switched to whatever the user asked for via this method. 83 #[allow(unused_variables)] // for cfg(not(unix)) switch_logging( &self, daemon: bool, with_output: bool ) -> Result<Option<LogOutput>, Failed>84 pub fn switch_logging( 85 &self, 86 daemon: bool, 87 with_output: bool 88 ) -> Result<Option<LogOutput>, Failed> { 89 let logger = match self.config.log_target { 90 #[cfg(unix)] 91 LogTarget::Default(fac) => { 92 if daemon { 93 self.syslog_logger(fac)? 94 } 95 else { 96 self.stderr_logger(false) 97 } 98 } 99 #[cfg(unix)] 100 LogTarget::Syslog(fac) => { 101 self.syslog_logger(fac)? 102 } 103 LogTarget::Stderr => { 104 self.stderr_logger(daemon) 105 } 106 LogTarget::File(ref path) => { 107 self.file_logger(path)? 108 } 109 }; 110 let (logger, res) = if with_output { 111 let (tx, res) = LogOutput::new(); 112 let logger = logger.chain(tx); 113 (logger, Some(res)) 114 } 115 else { 116 (logger, None) 117 }; 118 119 log_reroute::reroute_boxed(logger.into_log().1); 120 log::set_max_level(self.config.log_level); 121 Ok(res) 122 } 123 124 /// Creates a syslog logger and configures correctly. 125 #[cfg(unix)] syslog_logger( &self, facility: syslog::Facility ) -> Result<fern::Dispatch, Failed>126 fn syslog_logger( 127 &self, 128 facility: syslog::Facility 129 ) -> Result<fern::Dispatch, Failed> { 130 let process = std::env::current_exe().ok().and_then(|path| 131 path.file_name() 132 .and_then(std::ffi::OsStr::to_str) 133 .map(ToString::to_string) 134 ).unwrap_or_else(|| String::from("routinator")); 135 let formatter = syslog::Formatter3164 { 136 facility, 137 hostname: None, 138 process, 139 pid: nix::unistd::getpid().as_raw() 140 }; 141 let logger = syslog::unix(formatter.clone()).or_else(|_| { 142 syslog::tcp(formatter.clone(), ("127.0.0.1", 601)) 143 }).or_else(|_| { 144 syslog::udp(formatter, ("127.0.0.1", 0), ("127.0.0.1", 514)) 145 }); 146 match logger { 147 Ok(logger) => { 148 Ok(self.fern_logger(false).chain( 149 Box::new(syslog::BasicLogger::new(logger)) 150 as Box::<dyn log::Log> 151 )) 152 } 153 Err(err) => { 154 error!("Cannot connect to syslog: {}", err); 155 Err(Failed) 156 } 157 } 158 } 159 160 /// Creates a stderr logger. 161 /// 162 /// If we are in daemon mode, we add a timestamp to the output. stderr_logger(&self, daemon: bool) -> fern::Dispatch163 fn stderr_logger(&self, daemon: bool) -> fern::Dispatch { 164 self.fern_logger(daemon).chain(io::stderr()) 165 } 166 167 /// Creates a file logger using the file provided by `path`. file_logger(&self, path: &Path) -> Result<fern::Dispatch, Failed>168 fn file_logger(&self, path: &Path) -> Result<fern::Dispatch, Failed> { 169 let file = match fern::log_file(path) { 170 Ok(file) => file, 171 Err(err) => { 172 error!( 173 "Failed to open log file '{}': {}", 174 path.display(), err 175 ); 176 return Err(Failed) 177 } 178 }; 179 Ok(self.fern_logger(true).chain(file)) 180 } 181 182 /// Creates and returns a fern logger. fern_logger(&self, timestamp: bool) -> fern::Dispatch183 fn fern_logger(&self, timestamp: bool) -> fern::Dispatch { 184 let mut res = fern::Dispatch::new(); 185 if timestamp { 186 res = res.format(|out, message, _record| { 187 out.finish(format_args!( 188 "{} {} {}", 189 chrono::Local::now().format("[%Y-%m-%d %H:%M:%S]"), 190 _record.module_path().unwrap_or(""), 191 message 192 )) 193 }); 194 } 195 res = res 196 .level(self.config.log_level) 197 .level_for("rustls", LevelFilter::Error); 198 if self.config.log_level == LevelFilter::Debug { 199 res = res 200 .level_for("tokio_reactor", LevelFilter::Info) 201 .level_for("hyper", LevelFilter::Info) 202 .level_for("reqwest", LevelFilter::Info) 203 .level_for("h2", LevelFilter::Info) 204 .level_for("sled", LevelFilter::Info); 205 } 206 res 207 } 208 } 209 210 211 /// # System Service 212 /// 213 impl Process { 214 /// Sets up the system service. 215 /// 216 /// If `detach` is `true`, the service will detach from the current 217 /// process and keep running in the background. 218 /// 219 /// After the method returns, we will be running in the final process 220 /// but still have the same privileges as when we were initially started. 221 /// Whether there is still a terminal and standard stream available 222 /// depends on the config. 223 /// 224 /// This method may encounter and log errors after detaching. You should 225 /// therefore call `switch_logging` before this method. setup_service(&mut self, detach: bool) -> Result<(), Failed>226 pub fn setup_service(&mut self, detach: bool) -> Result<(), Failed> { 227 self.service.as_mut().unwrap().setup_service(&self.config, detach) 228 } 229 230 /// Drops privileges. 231 /// 232 /// If requested via the config, this method will drop all potentially 233 /// elevated privileges. This may include loosing root or system 234 /// administrator permissions and change the file system root. drop_privileges(&mut self) -> Result<(), Failed>235 pub fn drop_privileges(&mut self) -> Result<(), Failed> { 236 self.service.take().unwrap().drop_privileges(&mut self.config) 237 } 238 } 239 240 241 /// # Directory Management 242 /// 243 impl Process { 244 /// Creates the cache directory. 245 /// 246 /// This will also change ownership of the directory if necessary. create_cache_dir(&self) -> Result<(), Failed>247 pub fn create_cache_dir(&self) -> Result<(), Failed> { 248 if let Err(err) = fs::create_dir_all(&self.config.cache_dir) { 249 error!("Fatal: failed to create cache directory {}: {}", 250 self.config.cache_dir.display(), err 251 ); 252 return Err(Failed) 253 } 254 ServiceImpl::prepare_cache_dir(&self.config) 255 } 256 } 257 258 259 /// # Tokio Runtime 260 /// 261 impl Process { 262 /// Returns a Tokio runtime based on the configuration. runtime(&self) -> Result<Runtime, Failed>263 pub fn runtime(&self) -> Result<Runtime, Failed> { 264 Runtime::new().map_err(|err| { 265 error!("Failed to create runtime: {}", err); 266 Failed 267 }) 268 } 269 270 /// Runs a future to completion atop a Tokio runtime. block_on<F: Future>(&self, future: F) -> Result<F::Output, Failed>271 pub fn block_on<F: Future>(&self, future: F) -> Result<F::Output, Failed> { 272 Ok(self.runtime()?.block_on(future)) 273 } 274 } 275 276 277 //------------ LogOutput ----------------------------------------------------- 278 279 #[derive(Debug)] 280 pub struct LogOutput { 281 queue: Mutex<mpsc::Receiver<String>>, 282 current: RwLock<(Bytes, DateTime<Utc>)>, 283 } 284 285 impl LogOutput { new() -> (mpsc::Sender<String>, Self)286 fn new() -> (mpsc::Sender<String>, Self) { 287 let (tx, rx) = mpsc::channel(); 288 let res = LogOutput { 289 queue: Mutex::new(rx), 290 current: RwLock::new(( 291 "Initial validation ongoing. Please wait.".into(), 292 Utc::now() 293 )) 294 }; 295 (tx, res) 296 } 297 start(&self)298 pub fn start(&self) { 299 self.current.write().expect("Log lock got poisoned").1 = Utc::now(); 300 } 301 flush(&self)302 pub fn flush(&self) { 303 let queue = self.queue.lock().expect("Log queue lock got poisoned"); 304 let started = self.current.read().expect("Log lock got poisoned").1; 305 306 let mut content = format!( 307 "Log from validation run started at {}\n\n", started 308 ); 309 for item in queue.try_iter() { 310 content.push_str(&item) 311 } 312 self.current.write().expect("Log lock got poisoned").0 = content.into(); 313 } 314 get_output(&self) -> Bytes315 pub fn get_output(&self) -> Bytes { 316 self.current.read().expect("Log lock got poisoned").0.clone() 317 } 318 } 319 320 321 //------------ Platform-dependent Service Implementation --------------------- 322 323 #[cfg(unix)] 324 use self::unix::ServiceImpl; 325 326 #[cfg(not(unix))] 327 use self::noop::ServiceImpl; 328 329 330 /// Unix “Service.” 331 /// 332 /// This implementation is based on the 333 /// [daemonize](https://github.com/knsd/daemonize) crate. 334 /// 335 #[cfg(unix)] 336 mod unix { 337 use std::env::set_current_dir; 338 use std::ffi::CString; 339 use std::os::unix::io::RawFd; 340 use std::path::Path; 341 use log::error; 342 use nix::libc; 343 use nix::fcntl::{flock, open, FlockArg, OFlag}; 344 use nix::unistd::{ 345 chown, chroot, fork, getpid, setgid, setuid, write, Gid, Uid 346 }; 347 use nix::sys::stat::Mode; 348 use crate::config::Config; 349 use crate::error::Failed; 350 351 #[derive(Debug, Default)] 352 pub struct ServiceImpl { 353 pid_file: Option<RawFd>, 354 uid: Option<Uid>, 355 gid: Option<Gid>, 356 } 357 358 impl ServiceImpl { new(_config: &Config) -> Self359 pub fn new(_config: &Config) -> Self { 360 ServiceImpl::default() 361 } 362 setup_service( &mut self, config: &Config, detach: bool ) -> Result<(), Failed>363 pub fn setup_service( 364 &mut self, config: &Config, detach: bool 365 ) -> Result<(), Failed> { 366 if let Some(pid_file) = config.pid_file.as_ref() { 367 self.create_pid_file(pid_file)? 368 } 369 if detach { 370 self.perform_fork()? 371 } 372 if let Some(path) = config.working_dir.as_ref() { 373 if let Err(err) = set_current_dir(&path) { 374 error!("Fatal: failed to set working directory {}: {}", 375 path.display(), err 376 ); 377 return Err(Failed) 378 } 379 } 380 // set_sid 381 // umask 382 if detach { 383 self.perform_fork()? 384 } 385 // redirect_standard_streams 386 self.uid = Self::get_user(config)?; 387 self.gid = Self::get_group(config)?; 388 // chown_pid_file 389 390 Ok(()) 391 } 392 drop_privileges( self, config: &mut Config ) -> Result<(), Failed>393 pub fn drop_privileges( 394 self, config: &mut Config 395 ) -> Result<(), Failed> { 396 config.adjust_chroot_paths()?; 397 if let Some(path) = config.chroot.as_ref() { 398 if let Err(err) = chroot(path) { 399 error!("Fatal: cannot chroot to '{}': {}'", 400 path.display(), err 401 ); 402 return Err(Failed) 403 } 404 } 405 if let Some(gid) = self.gid { 406 if let Err(err) = setgid(gid) { 407 error!("Fatal: failed to set group: {}", err); 408 return Err(Failed) 409 } 410 } 411 if let Some(uid) = self.uid { 412 if let Err(err) = setuid(uid) { 413 error!("Fatal: failed to set user: {}", err); 414 return Err(Failed) 415 } 416 } 417 self.write_pid_file()?; 418 419 Ok(()) 420 } 421 create_pid_file(&mut self, path: &Path) -> Result<(), Failed>422 fn create_pid_file(&mut self, path: &Path) -> Result<(), Failed> { 423 let fd = match open( 424 path, 425 OFlag::O_WRONLY | OFlag::O_CREAT, 426 Mode::from_bits_truncate(0o666) 427 ) { 428 Ok(fd) => fd, 429 Err(err) => { 430 error!("Fatal: failed to create PID file {}: {}", 431 path.display(), err 432 ); 433 return Err(Failed) 434 } 435 }; 436 if let Err(err) = flock(fd, FlockArg::LockExclusiveNonblock) { 437 error!("Fatal: cannot lock PID file {}: {}", 438 path.display(), err 439 ); 440 return Err(Failed) 441 } 442 self.pid_file = Some(fd); 443 Ok(()) 444 } 445 write_pid_file(&self) -> Result<(), Failed>446 fn write_pid_file(&self) -> Result<(), Failed> { 447 if let Some(pid_file) = self.pid_file { 448 let pid = format!("{}", getpid()); 449 match write(pid_file, pid.as_bytes()) { 450 Ok(len) if len == pid.len() => {} 451 Ok(_) => { 452 error!( 453 "Fatal: failed to write PID to PID file: \ 454 short write" 455 ); 456 return Err(Failed) 457 } 458 Err(err) => { 459 error!( 460 "Fatal: failed to write PID to PID file: {}", err 461 ); 462 return Err(Failed) 463 } 464 } 465 } 466 Ok(()) 467 } 468 perform_fork(&self) -> Result<(), Failed>469 fn perform_fork(&self) -> Result<(), Failed> { 470 match unsafe { fork() } { 471 Ok(res) => { 472 if res.is_parent() { 473 std::process::exit(0) 474 } 475 Ok(()) 476 } 477 Err(err) => { 478 error!("Fatal: failed to detach: {}", err); 479 Err(Failed) 480 } 481 } 482 } 483 get_user(config: &Config) -> Result<Option<Uid>, Failed>484 fn get_user(config: &Config) -> Result<Option<Uid>, Failed> { 485 let name = match config.user.as_ref() { 486 Some(name) => name, 487 None => return Ok(None) 488 }; 489 let cname = match CString::new(name.clone()) { 490 Ok(name) => name, 491 Err(_) => { 492 error!("Fatal: invalid user ID '{}'", name); 493 return Err(Failed) 494 } 495 }; 496 497 let uid = unsafe { 498 let ptr = libc::getpwnam(cname.as_ptr() as *const libc::c_char); 499 if ptr.is_null() { 500 None 501 } 502 else { 503 let s = &*ptr; 504 Some(s.pw_uid) 505 } 506 }; 507 match uid { 508 Some(uid) => Ok(Some(Uid::from_raw(uid))), 509 None => { 510 error!("Fatal: unknown user ID '{}'", name); 511 Err(Failed) 512 } 513 } 514 } 515 get_group(config: &Config) -> Result<Option<Gid>, Failed>516 fn get_group(config: &Config) -> Result<Option<Gid>, Failed> { 517 let name = match config.group.as_ref() { 518 Some(name) => name, 519 None => return Ok(None) 520 }; 521 let cname = match CString::new(name.clone()) { 522 Ok(name) => name, 523 Err(_) => { 524 error!("Fatal: invalid user ID '{}'", name); 525 return Err(Failed) 526 } 527 }; 528 529 let gid = unsafe { 530 let ptr = libc::getgrnam(cname.as_ptr() as *const libc::c_char); 531 if ptr.is_null() { 532 None 533 } 534 else { 535 let s = &*ptr; 536 Some(s.gr_gid) 537 } 538 }; 539 match gid { 540 Some(gid) => Ok(Some(Gid::from_raw(gid))), 541 None => { 542 error!("Fatal: unknown group ID '{}'", name); 543 Err(Failed) 544 } 545 } 546 } 547 prepare_cache_dir(config: &Config) -> Result<(), Failed>548 pub fn prepare_cache_dir(config: &Config) -> Result<(), Failed> { 549 let uid = Self::get_user(config)?; 550 let gid = Self::get_group(config)?; 551 if uid.is_some() || gid.is_some() { 552 if let Err(err) = chown(&config.cache_dir, uid, gid) { 553 error!( 554 "Fatal: failed to change ownership of cache dir \ 555 {}: {}", 556 config.cache_dir.display(), 557 err 558 ); 559 return Err(Failed) 560 } 561 } 562 Ok(()) 563 } 564 } 565 } 566 567 #[cfg(not(unix))] 568 mod noop { 569 use crate::error::Failed; 570 use crate::config::Config; 571 572 pub struct ServiceImpl; 573 574 impl ServiceImpl { new(_config: &Config) -> Self575 pub fn new(_config: &Config) -> Self { 576 ServiceImpl 577 } 578 setup_service( &mut self, _config: &Config, _detach: bool ) -> Result<(), Failed>579 pub fn setup_service( 580 &mut self, _config: &Config, _detach: bool 581 ) -> Result<(), Failed> { 582 Ok(()) 583 } 584 drop_privileges( self, _config: &mut Config ) -> Result<(), Failed>585 pub fn drop_privileges( 586 self, _config: &mut Config 587 ) -> Result<(), Failed> { 588 Ok(()) 589 } 590 prepare_cache_dir(_config: &Config) -> Result<(), Failed>591 pub fn prepare_cache_dir(_config: &Config) -> Result<(), Failed> { 592 Ok(()) 593 } 594 } 595 } 596