From 2c6d13f3fac35b901b39a851eea2662ae6d02bf1 Mon Sep 17 00:00:00 2001 From: huyubiao Date: Thu, 29 Jun 2023 21:40:53 +0800 Subject: [PATCH] fix: init does not enter the freeze during normal running --- core/bin/unit/notify.rs | 4 +- exts/init/src/main.rs | 21 ++-- exts/init/src/runtime/comm.rs | 163 ++++++++++++++++++++----------- exts/init/src/runtime/epoll.rs | 14 ++- exts/init/src/runtime/mod.rs | 51 ++++------ exts/init/src/runtime/signals.rs | 95 ++++++++---------- exts/init/src/runtime/timer.rs | 58 ++++++++--- libs/constants/src/lib.rs | 3 + 8 files changed, 226 insertions(+), 183 deletions(-) diff --git a/core/bin/unit/notify.rs b/core/bin/unit/notify.rs index f824725..d23a725 100644 --- a/core/bin/unit/notify.rs +++ b/core/bin/unit/notify.rs @@ -109,7 +109,7 @@ impl NotifyManager { } } -const NOTIFY_INVALID_FD: i32 = -1; +use constants::INVALID_FD; const NOTIFY_INVALID_PID: libc::pid_t = -1; struct Notify { @@ -135,7 +135,7 @@ impl Notify { rentry: Rc::clone(rentryr), db: Rc::clone(dbr), config: Rc::clone(configr), - fd: RefCell::new(NOTIFY_INVALID_FD), + fd: RefCell::new(INVALID_FD), } } diff --git a/exts/init/src/main.rs b/exts/init/src/main.rs index 587077e..fbd77b9 100644 --- a/exts/init/src/main.rs +++ b/exts/init/src/main.rs @@ -23,21 +23,14 @@ fn main() { // Run: Monitor the sysmaster's liveliness and acceptance of message. // Unrecover: On-site problem collection or recreate new sysmaster. match RunTime::new(cmd) { - Ok(mut run_time) => { - loop { - let state = run_time.state(); - let ret = match state { - InitState::Reexec => run_time.reexec(), - InitState::Run => run_time.run(), - InitState::Unrecover => run_time.unrecover(), - }; - if let Err(err) = ret { - eprintln!("Failed to {:?}:{:?} ", state, err); - break; - } + Ok(mut run_time) => loop { + let state = run_time.state(); + match state { + InitState::Reexec => run_time.reexec(), + InitState::Run => run_time.run(), + InitState::Unrecover => run_time.unrecover(), } - run_time.clear(); - } + }, Err(err) => eprintln!( "Failed to new init, it may be necessary to run it as root :{:?}", err diff --git a/exts/init/src/runtime/comm.rs b/exts/init/src/runtime/comm.rs index 1a0d0ca..8f71511 100644 --- a/exts/init/src/runtime/comm.rs +++ b/exts/init/src/runtime/comm.rs @@ -14,18 +14,18 @@ use super::epoll::Epoll; use super::timer::Timer; use nix::errno::Errno; use nix::sys::epoll::EpollEvent; +use nix::sys::inotify::{AddWatchFlags, InitFlags, Inotify, WatchDescriptor}; use nix::sys::socket::{self, AddressFamily, SockFlag, SockType, UnixAddr}; use nix::unistd; +use std::os::unix::io::AsRawFd; use std::os::unix::prelude::RawFd; use std::rc::Rc; -use std::str; -use std::{fs, path::PathBuf}; +use std::{fs, path::PathBuf, str}; const LISTEN_BACKLOG: usize = 10; -const INVALID_FD: i32 = -1; const ACCEPT_COUNT: i32 = 3; const BUF_SIZE: usize = 16; //The communication string length is fixed to 16 characters. -use constants::{ALIVE, INIT_SOCKET}; +use constants::{ALIVE, INIT_SOCKET, INVALID_FD}; pub struct Comm { epoll: Rc, @@ -33,6 +33,8 @@ pub struct Comm { connect_fd: RawFd, online_fd: RawFd, // Specsify either listen_fd or connect_fd. timer: Timer, + inotify: Inotify, + wd: WatchDescriptor, } #[derive(PartialEq, Eq)] @@ -47,28 +49,7 @@ impl Comm { pub fn new(epoll: &Rc, time_wait: i64, time_cnt: i64) -> Result { let timer = Timer::new(epoll, time_wait, time_cnt)?; - let sock_path = PathBuf::from(INIT_SOCKET); - let listen_fd = socket::socket( - AddressFamily::Unix, - SockType::Stream, - SockFlag::SOCK_CLOEXEC, - None, - )?; - - let parent_path = sock_path.as_path().parent(); - match parent_path { - Some(path) => fs::create_dir_all(path) - .map_err(|e| Errno::from_i32(e.raw_os_error().unwrap_or(Errno::EINVAL as i32)))?, - None => return Err(Errno::EINVAL), - } - - if let Err(e) = unistd::unlink(&sock_path) { - eprintln!("Failed to unlink path:{:?}, error:{}", sock_path, e); - } - - let addr = UnixAddr::new(&sock_path)?; - socket::bind(listen_fd, &addr)?; - socket::listen(listen_fd, LISTEN_BACKLOG)?; + let (listen_fd, inotify, wd) = create_listen_fd(epoll)?; let mut comm = Comm { epoll: epoll.clone(), @@ -76,51 +57,50 @@ impl Comm { connect_fd: INVALID_FD, online_fd: INVALID_FD, timer, + inotify, + wd, }; comm.set_online_fd(comm.listen_fd)?; - Ok(comm) } pub fn is_fd(&self, fd: RawFd) -> bool { - if fd == self.online_fd || fd == self.timer.fd() { - return true; - } - false + fd == self.online_fd || fd == self.timer.fd() || fd == self.inotify.as_raw_fd() } - pub fn proc(&mut self, event: EpollEvent) -> Result { + pub fn proc(&mut self, event: EpollEvent) -> CommType { if self.timer.fd() as u64 == event.data() { - if self.timer.is_time_out(event)? { - return Ok(CommType::PipTMOUT); + if self.timer.is_time_out(event) { + return CommType::PipTMOUT; } - return Ok(CommType::Succeed); + return CommType::Succeed; + } + + if self.inotify.as_raw_fd() as u64 == event.data() { + // Dont self.inotify.read_events(), because if recover fails, event can be retrieved to recover again. + return self.recover(); + } + + // When the program runs normally, listen_fd will not be closed, + // but connect_fd will be closed during listening. + if self.listen_fd as u64 == event.data() && self.epoll.is_err(event) { + return self.recover(); } - // When online_fd fails, ensure that connect_fd is closed, and then execute again after timeout. if self.online_fd as u64 == event.data() { match self.online_fd { - x if x == self.listen_fd => return self.listen_proc(event), + x if x == self.listen_fd => return self.listen_proc(), x if x == self.connect_fd => return self.connect_proc(event), _ => {} } } - Ok(CommType::Succeed) + CommType::Succeed } pub fn finish(&mut self) { _ = self.set_online_fd(self.listen_fd); - } - - pub fn clear(&mut self) { - self.epoll.safe_close(self.listen_fd); - self.listen_fd = INVALID_FD; - - self.epoll.safe_close(self.connect_fd); - self.connect_fd = INVALID_FD; - - self.timer.clear(); + self.timer.reset(); } fn connect(&mut self) -> Result<(), Errno> { @@ -223,21 +203,18 @@ impl Comm { Ok(()) } - fn listen_proc(&mut self, event: EpollEvent) -> Result { - if self.epoll.is_err(event) { - return Err(Errno::EINVAL); - } + fn listen_proc(&mut self) -> CommType { if self.connect().is_err() { - return Ok(CommType::PipOFF); + return CommType::PipOFF; } self.timer.reset(); - Ok(CommType::PipON) + CommType::PipON } - fn connect_proc(&mut self, event: EpollEvent) -> Result { + fn connect_proc(&mut self, event: EpollEvent) -> CommType { if self.epoll.is_err(event) { _ = self.set_online_fd(self.listen_fd); - return Ok(CommType::PipOFF); + return CommType::PipOFF; } match self.recv_msg() { Ok(buf) => { @@ -252,6 +229,78 @@ impl Comm { unistd::sleep(1); } } - Ok(CommType::Succeed) + CommType::Succeed } + + fn recover(&mut self) -> CommType { + match create_listen_fd(&self.epoll) { + Ok((listen_fd, inotify, wd)) => { + self.epoll.safe_close(self.listen_fd); + self.epoll.safe_close(self.inotify.as_raw_fd()); + self.listen_fd = listen_fd; + self.inotify = inotify; + self.wd = wd; + eprintln!("comm recover"); + if self.online_fd == self.connect_fd { + // The socket file(INIT_SOCKET) cannot be used when connecting, + // so recreate the socket file(INIT_SOCKET) and return success. + return CommType::Succeed; + } else { + // If init is in the listening state, + // the sysmaster cannot be connected through the old socket file(INIT_SOCKET) at this time, + // we must recreate the socket file and then reexec the sysmaster. + return CommType::PipTMOUT; + } + } + Err(e) => { + eprintln!("Failed to create_listen_fd:{:?}", e); + } + } + CommType::Succeed + } +} + +impl Drop for Comm { + fn drop(&mut self) { + self.epoll.safe_close(self.listen_fd); + self.listen_fd = INVALID_FD; + + self.epoll.safe_close(self.connect_fd); + self.connect_fd = INVALID_FD; + + let _ = self.inotify.rm_watch(self.wd); + self.epoll.safe_close(self.inotify.as_raw_fd()); + } +} + +fn create_listen_fd(epoll: &Rc) -> Result<(i32, Inotify, WatchDescriptor), Errno> { + let listen_fd = socket::socket( + AddressFamily::Unix, + SockType::Stream, + SockFlag::SOCK_CLOEXEC, + None, + )?; + + let sock_path = PathBuf::from(INIT_SOCKET); + let parent_path = sock_path.as_path().parent(); + match parent_path { + Some(path) => fs::create_dir_all(path) + .map_err(|e| Errno::from_i32(e.raw_os_error().unwrap_or(Errno::EINVAL as i32)))?, + None => return Err(Errno::EINVAL), + } + + if let Err(e) = unistd::unlink(&sock_path) { + eprintln!("Failed to unlink path:{:?}, error:{}", sock_path, e); + } + + let addr = UnixAddr::new(&sock_path)?; + socket::bind(listen_fd, &addr)?; + socket::listen(listen_fd, LISTEN_BACKLOG)?; + + let inotify = Inotify::init(InitFlags::all())?; + + let wd = inotify.add_watch(INIT_SOCKET, AddWatchFlags::IN_ALL_EVENTS)?; + + epoll.register(inotify.as_raw_fd())?; + Ok((listen_fd, inotify, wd)) } diff --git a/exts/init/src/runtime/epoll.rs b/exts/init/src/runtime/epoll.rs index ad7475f..6c21bbc 100644 --- a/exts/init/src/runtime/epoll.rs +++ b/exts/init/src/runtime/epoll.rs @@ -10,6 +10,7 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use constants::INVALID_FD; use nix::errno::Errno; use nix::sys::epoll::{self, EpollEvent, EpollFlags, EpollOp}; use nix::unistd; @@ -21,7 +22,7 @@ pub struct Epoll { impl Epoll { pub(crate) fn new() -> Result { - let epoll_fd = epoll::epoll_create1(epoll::EpollCreateFlags::empty())?; + let epoll_fd = epoll::epoll_create1(epoll::EpollCreateFlags::EPOLL_CLOEXEC)?; Ok(Epoll { epoll_fd }) } @@ -61,10 +62,6 @@ impl Epoll { false } - pub(crate) fn clear(&self) { - self.safe_close(self.epoll_fd); - } - pub(crate) fn safe_close(&self, fd: RawFd) { if fd <= 0 { return; @@ -75,3 +72,10 @@ impl Epoll { }; } } + +impl Drop for Epoll { + fn drop(&mut self) { + self.safe_close(self.epoll_fd); + self.epoll_fd = INVALID_FD; + } +} diff --git a/exts/init/src/runtime/mod.rs b/exts/init/src/runtime/mod.rs index d11d8f2..e81a513 100644 --- a/exts/init/src/runtime/mod.rs +++ b/exts/init/src/runtime/mod.rs @@ -81,7 +81,7 @@ impl RunTime { self.state } - pub fn reexec(&mut self) -> Result<(), Errno> { + pub fn reexec(&mut self) { if self.need_reexec { self.reexec_manager(); } @@ -89,43 +89,33 @@ impl RunTime { let event = self.epoll.wait_one(); let fd = event.data() as RawFd; match fd { - _x if self.comm.is_fd(fd) => self.reexec_comm_dispatch(event)?, - _x if self.signals.is_fd(fd) => self.reexec_signal_dispatch(event)?, + _x if self.comm.is_fd(fd) => self.reexec_comm_dispatch(event), + _x if self.signals.is_fd(fd) => self.reexec_signal_dispatch(event), _ => self.epoll.safe_close(fd), } - Ok(()) } - pub fn run(&mut self) -> Result<(), Errno> { + pub fn run(&mut self) { let event = self.epoll.wait_one(); let fd = event.data() as RawFd; match fd { - _x if self.comm.is_fd(fd) => self.run_comm_dispatch(event)?, - _x if self.signals.is_fd(fd) => self.run_signal_dispatch(event)?, + _x if self.comm.is_fd(fd) => self.run_comm_dispatch(event), + _x if self.signals.is_fd(fd) => self.run_signal_dispatch(event), _ => self.epoll.safe_close(fd), } - Ok(()) } - pub fn unrecover(&mut self) -> Result<(), Errno> { + pub fn unrecover(&mut self) { let event = self.epoll.wait_one(); let fd = event.data() as RawFd; match fd { _x if self.comm.is_fd(fd) => self.unrecover_comm_dispatch(event), - _x if self.signals.is_fd(fd) => self.unrecover_signal_dispatch(event)?, + _x if self.signals.is_fd(fd) => self.unrecover_signal_dispatch(event), _ => self.epoll.safe_close(fd), } - Ok(()) - } - - pub fn clear(&mut self) { - self.comm.clear(); - self.signals.clear(); - self.epoll.clear(); } pub fn reexec_self(&mut self) { - self.clear(); let mut args = Vec::new(); let mut init_path = CString::new("/usr/bin/init").unwrap(); if let Some(str) = std::env::args().next() { @@ -154,17 +144,16 @@ impl RunTime { unsafe { libc::kill(self.sysmaster_pid.into(), libc::SIGABRT) }; } - fn reexec_comm_dispatch(&mut self, event: EpollEvent) -> Result<(), Errno> { - match self.comm.proc(event)? { + fn reexec_comm_dispatch(&mut self, event: EpollEvent) { + match self.comm.proc(event) { CommType::PipON => self.state = InitState::Run, CommType::PipTMOUT => self.need_reexec = true, _ => {} } - Ok(()) } - fn run_comm_dispatch(&mut self, event: EpollEvent) -> Result<(), Errno> { - match self.comm.proc(event)? { + fn run_comm_dispatch(&mut self, event: EpollEvent) { + match self.comm.proc(event) { CommType::PipOFF => self.state = InitState::Reexec, CommType::PipTMOUT => { self.state = InitState::Reexec; @@ -172,15 +161,14 @@ impl RunTime { } _ => {} } - Ok(()) } fn unrecover_comm_dispatch(&mut self, event: EpollEvent) { _ = self.comm.proc(event); } - fn reexec_signal_dispatch(&mut self, event: EpollEvent) -> Result<(), Errno> { - if let Some(siginfo) = self.signals.read(event)? { + fn reexec_signal_dispatch(&mut self, event: EpollEvent) { + if let Some(siginfo) = self.signals.read(event) { match siginfo { _x if self.signals.is_zombie(siginfo) => self.do_recycle(), _x if self.signals.is_restart(siginfo) => self.do_reexec(), @@ -188,11 +176,10 @@ impl RunTime { _ => {} } } - Ok(()) } - fn run_signal_dispatch(&mut self, event: EpollEvent) -> Result<(), Errno> { - if let Some(siginfo) = self.signals.read(event)? { + fn run_signal_dispatch(&mut self, event: EpollEvent) { + if let Some(siginfo) = self.signals.read(event) { match siginfo { _x if self.signals.is_zombie(siginfo) => self.do_recycle(), _x if self.signals.is_restart(siginfo) => self.do_reexec(), @@ -200,11 +187,10 @@ impl RunTime { _ => {} } } - Ok(()) } - fn unrecover_signal_dispatch(&mut self, event: EpollEvent) -> Result<(), Errno> { - if let Some(siginfo) = self.signals.read(event)? { + fn unrecover_signal_dispatch(&mut self, event: EpollEvent) { + if let Some(siginfo) = self.signals.read(event) { match siginfo { _x if self.signals.is_zombie(siginfo) => { self.signals.recycle_zombie(); @@ -216,7 +202,6 @@ impl RunTime { _ => {} } } - Ok(()) } fn change_to_unrecover(&mut self) { diff --git a/exts/init/src/runtime/signals.rs b/exts/init/src/runtime/signals.rs index 1c3059c..327ff3e 100644 --- a/exts/init/src/runtime/signals.rs +++ b/exts/init/src/runtime/signals.rs @@ -13,7 +13,8 @@ use super::epoll::Epoll; use nix::errno::Errno; use nix::sys::epoll::EpollEvent; -use nix::sys::signal::{SigmaskHow, Signal}; +use nix::sys::signal::{self, SigSet, SigmaskHow, Signal}; +use nix::sys::signalfd::{self, SfdFlags}; use nix::sys::wait::{self, Id, WaitPidFlag, WaitStatus}; use nix::unistd; use std::mem; @@ -21,42 +22,12 @@ use std::ops::Neg; use std::os::unix::io::RawFd; use std::rc::Rc; +use constants::INVALID_FD; use constants::{SIG_RESTART_MANAGER_OFFSET, SIG_RUN_UNRECOVER_OFFSET, SIG_SWITCH_ROOT_OFFSET}; -const INVALID_FD: i32 = -1; - -pub(crate) struct SigSet { - sigset: libc::sigset_t, -} - -impl SigSet { - /// Initialize to include nothing. - pub fn empty() -> SigSet { - let mut sigset = mem::MaybeUninit::zeroed(); - let _ = unsafe { libc::sigemptyset(sigset.as_mut_ptr()) }; - - unsafe { - SigSet { - sigset: sigset.assume_init(), - } - } - } - - /// Add the specified signal to the set. - pub fn add(&mut self, signal: libc::c_int) { - unsafe { - libc::sigaddset( - &mut self.sigset as *mut libc::sigset_t, - signal as libc::c_int, - ) - }; - } -} pub struct Signals { epoll: Rc, - signal_fd: RawFd, - set: SigSet, - oldset: SigSet, + pub signal_fd: RawFd, signals: Vec, zombie_signal: i32, restart_signal: i32, @@ -71,8 +42,6 @@ impl Signals { Signals { epoll: epoll.clone(), signal_fd: INVALID_FD, - set: SigSet::empty(), - oldset: SigSet::empty(), signals, zombie_signal: libc::SIGCHLD, unrecover_signal: libc::SIGRTMIN() + SIG_RUN_UNRECOVER_OFFSET, @@ -101,29 +70,25 @@ impl Signals { } pub fn create_signals_epoll(&mut self) -> Result<(), Errno> { - self.reset_sigset(); + self.signal_fd = self.reset_sigset()?; self.epoll.register(self.signal_fd)?; Ok(()) } - pub fn reset_sigset(&mut self) { + pub fn reset_sigset(&mut self) -> Result { + let mut sigset = SigSet::empty(); for sig in self.signals.clone() { - self.set.add(sig); - } - - unsafe { - libc::pthread_sigmask(libc::SIG_BLOCK, &self.set.sigset, &mut self.oldset.sigset); - self.signal_fd = libc::signalfd( - -1, - &mut self.set.sigset as *const libc::sigset_t, - libc::SFD_NONBLOCK, - ); + let signum: Signal = unsafe { std::mem::transmute(sig) }; + sigset.add(signum); } + let mut oldset = SigSet::empty(); + signal::pthread_sigmask(SigmaskHow::SIG_BLOCK, Some(&sigset), Some(&mut oldset))?; + signalfd::signalfd(-1, &sigset, SfdFlags::SFD_CLOEXEC | SfdFlags::SFD_NONBLOCK) } - pub fn read(&mut self, event: EpollEvent) -> Result, Errno> { + pub fn read(&mut self, event: EpollEvent) -> Option { if self.epoll.is_err(event) { - return Err(Errno::EIO); + return self.recover(); } let mut buffer = mem::MaybeUninit::::zeroed(); @@ -139,14 +104,14 @@ impl Signals { match res { x if x == size as isize => { let info = unsafe { buffer.assume_init() }; - Ok(Some(info)) + Some(info) } - x if x >= 0 => Ok(None), + x if x >= 0 => None, x => { let err = Errno::from_i32(x.neg() as i32); eprintln!("read_signals failed err:{:?}", err); unistd::sleep(1); - Ok(None) + None } } } @@ -191,13 +156,31 @@ impl Signals { } pub fn is_fd(&self, fd: RawFd) -> bool { - if fd == self.signal_fd { - return true; + fd == self.signal_fd + } + + fn recover(&mut self) -> Option { + match self.reset_sigset() { + Ok(signal_fd) => match self.epoll.register(signal_fd) { + Ok(_) => { + self.epoll.safe_close(self.signal_fd); + self.signal_fd = signal_fd; + eprintln!("signals recover"); + } + Err(e) => { + eprintln!("Failed to register signal_fd:{:?}", e); + } + }, + Err(e) => { + eprintln!("Failed to create_signals_epoll:{:?}", e); + } } - false + None } +} - pub fn clear(&mut self) { +impl Drop for Signals { + fn drop(&mut self) { self.epoll.safe_close(self.signal_fd); self.signal_fd = INVALID_FD; if let Err(e) = nix::sys::signal::pthread_sigmask( diff --git a/exts/init/src/runtime/timer.rs b/exts/init/src/runtime/timer.rs index f7b5f30..54fcc62 100644 --- a/exts/init/src/runtime/timer.rs +++ b/exts/init/src/runtime/timer.rs @@ -24,25 +24,18 @@ pub struct Timer { epoll: Rc, timer: TimerFd, current_cnt: i64, + time_wait: i64, time_cnt: i64, } impl Timer { pub fn new(epoll: &Rc, time_wait: i64, time_cnt: i64) -> Result { - let timer = TimerFd::new( - ClockId::CLOCK_REALTIME, - TimerFlags::TFD_NONBLOCK | TimerFlags::TFD_CLOEXEC, - )?; - timer.set( - Expiration::Interval(TimeSpec::seconds(time_wait)), - TimerSetTimeFlags::empty(), - )?; - - epoll.register(timer.as_raw_fd())?; + let timer = create_timer(epoll, time_wait)?; Ok(Timer { epoll: epoll.clone(), timer, current_cnt: 0, + time_wait, time_cnt, }) } @@ -56,18 +49,18 @@ impl Timer { } #[allow(clippy::wrong_self_convention)] - pub fn is_time_out(&mut self, event: EpollEvent) -> Result { + pub fn is_time_out(&mut self, event: EpollEvent) -> bool { if self.epoll.is_err(event) { - return Err(Errno::EIO); + return self.recover(); } self.flush(); self.current_cnt += 1; if self.time_cnt <= self.current_cnt { eprintln!("time out!"); self.reset(); - return Ok(true); + return true; } - Ok(false) + false } // reset timer. @@ -79,7 +72,40 @@ impl Timer { self.timer.as_raw_fd() } - pub fn clear(&mut self) { - self.epoll.safe_close(self.timer.as_raw_fd()); + fn recover(&mut self) -> bool { + match create_timer(&self.epoll, self.time_wait) { + Ok(timer) => { + // After successfully creating a new timer, recycle the old timer so that + // if create_timer fails, event can be retrieved to create_timer again. + // timer have drop, no need to manually close timer. + self.timer = timer; + eprintln!("timer recover"); + } + Err(e) => { + eprintln!("Failed to create_timer:{:?}", e); + } + } + // Here we believe that the system has encountered an exception, set it to timeout + true + } +} + +impl Drop for Timer { + fn drop(&mut self) { + // self.timer does not need to drop, because TimerFd have drop. } } + +fn create_timer(epoll: &Rc, time_wait: i64) -> Result { + let timer = TimerFd::new( + ClockId::CLOCK_REALTIME, + TimerFlags::TFD_NONBLOCK | TimerFlags::TFD_CLOEXEC, + )?; + timer.set( + Expiration::Interval(TimeSpec::seconds(time_wait)), + TimerSetTimeFlags::empty(), + )?; + + epoll.register(timer.as_raw_fd())?; + Ok(timer) +} diff --git a/libs/constants/src/lib.rs b/libs/constants/src/lib.rs index 6005067..1f84b40 100644 --- a/libs/constants/src/lib.rs +++ b/libs/constants/src/lib.rs @@ -29,3 +29,6 @@ pub const SCTL_SOCKET: &str = "/run/sysmaster/sctl"; /// Default log file path when LogTarget is configured to "file" pub const LOG_FILE_PATH: &str = "/var/log/sysmaster/sysmaster.log"; + +/// invalid fd +pub const INVALID_FD: i32 = -1; -- 2.33.0