1 use std::fs::{self, File}; 2 use std::io::Read; 3 use std::os::fd::FromRawFd; 4 use std::sync::{Arc, Mutex}; 5 6 use lazy_static::lazy_static; 7 8 use crate::error::ErrorFormat; 9 use crate::manager::ctl_manager::CtlManager; 10 11 use super::ctl_parser::{CommandOperation, CtlParser, Pattern}; 12 use super::{ctl_path, DRAGON_REACH_CTL_PIPE}; 13 14 lazy_static! { 15 static ref CTL_READER: Mutex<Arc<File>> = { 16 let file = Systemctl::init_listener(); 17 Mutex::new(Arc::new(file)) 18 }; 19 } 20 21 pub struct Command { 22 pub(crate) operation: CommandOperation, 23 pub(crate) args: Option<Vec<String>>, 24 pub(crate) patterns: Vec<Pattern>, 25 } 26 27 impl Command { 28 #[allow(dead_code)] 29 fn new(op: CommandOperation, patterns: Vec<Pattern>, args: Option<Vec<String>>) -> Self { 30 Command { 31 operation: op, 32 args, 33 patterns: patterns, 34 } 35 } 36 } 37 38 impl Default for Command { 39 fn default() -> Self { 40 Command { 41 operation: CommandOperation::None, 42 args: None, 43 patterns: Vec::new(), 44 } 45 } 46 } 47 48 pub struct Systemctl; 49 50 impl Systemctl { 51 /// # 初始化系统服务控制 - 初始化系统服务控制管道 52 pub fn init() { 53 Self::init_ctl_pipe(); 54 } 55 /// # 初始化监听器 - 初始化系统服务控制命令监听器 56 /// 57 /// 打开系统服务控制命令的管道文件描述符,并设置为非阻塞模式。 58 /// 59 pub fn init_listener() -> File { 60 let fd = unsafe { libc::open(ctl_path().as_ptr(), libc::O_RDONLY | libc::O_NONBLOCK) }; 61 if fd < 0 { 62 panic!("open ctl pipe error"); 63 } 64 unsafe { File::from_raw_fd(fd) } 65 } 66 /// # 监听控制命令 - 监听系统服务控制命令 67 /// 68 /// 持续从系统服务控制管道中读取命令。 69 /// 70 pub fn ctl_listen() { 71 let mut guard = CTL_READER.lock().unwrap(); 72 let mut s = String::new(); 73 if let Ok(size) = guard.read_to_string(&mut s) { 74 if size == 0 { 75 return; 76 } 77 match CtlParser::parse_ctl(&s) { 78 Ok(cmd) => { 79 let _ = CtlManager::exec_ctl(cmd); 80 } 81 Err(err) => { 82 eprintln!("parse tcl command error: {}", err.error_format()); 83 } 84 } 85 } 86 } 87 88 /// # 检查控制管道是否存在 - 检查系统服务控制管道文件是否存在 89 /// 90 /// 返回管道文件是否存在。 91 /// 92 fn is_ctl_exists() -> bool { 93 if let Ok(metadata) = fs::metadata(DRAGON_REACH_CTL_PIPE) { 94 metadata.is_file() 95 } else { 96 false 97 } 98 } 99 100 fn init_ctl_pipe() { 101 if !Self::is_ctl_exists() { 102 let path = ctl_path(); 103 let ret = unsafe { libc::mkfifo(path.as_ptr(), 0o666) }; 104 if ret != 0 { 105 // 创建管道失败打日志 106 panic!("create ctl pipe failed, err: {ret}"); 107 } 108 } 109 } 110 } 111