From 714eeceecbe09fd8855cd84279dc13e45a093fb2 Mon Sep 17 00:00:00 2001 From: chenjiayi Date: Thu, 16 Nov 2023 08:07:07 +0800 Subject: [PATCH 064/103] feature(devmaster): add devctl control subcommand to let devmaster exit Also decouple the configuration path into the parameter of run_daemon function for unit test. --- exts/devmaster/src/bin/devctl/daemon/mod.rs | 37 ++++++++++++++++++- exts/devmaster/src/bin/devctl/main.rs | 19 +++++++++- .../src/lib/framework/control_manager.rs | 9 ++++- exts/devmaster/src/lib/framework/devmaster.rs | 5 ++- 4 files changed, 63 insertions(+), 7 deletions(-) diff --git a/exts/devmaster/src/bin/devctl/daemon/mod.rs b/exts/devmaster/src/bin/devctl/daemon/mod.rs index 6dfe9718..3e4bd96d 100644 --- a/exts/devmaster/src/bin/devctl/daemon/mod.rs +++ b/exts/devmaster/src/bin/devctl/daemon/mod.rs @@ -38,10 +38,10 @@ fn notify(unset_env: bool, msg: String) -> std::io::Result<()> { } } -pub fn run_daemon() { +pub fn run_daemon(config_path: &str) { let events = Rc::new(Events::new().unwrap()); - let devmaster = Devmaster::new(events); + let devmaster = Devmaster::new(config_path, events); if let Err(e) = notify(false, "READY=1\n".to_string()) { log::warn!("Failed to notify pid 1: {}", e); @@ -51,3 +51,36 @@ pub fn run_daemon() { devmaster.as_ref().borrow().exit(); } + +#[cfg(test)] +mod test { + use super::*; + use device::{Device, DeviceAction}; + use libdevmaster::framework::control_manager::CONTROL_MANAGER_LISTEN_ADDR; + use std::io::Write; + + #[test] + fn test_run_daemon() { + /* Require root privilege, skip in ci environment. */ + let dev = Device::from_subsystem_sysname("net", "lo").unwrap(); + if dev.trigger(DeviceAction::Change).is_err() { + return; + } + + std::thread::spawn(|| { + std::thread::sleep(std::time::Duration::from_secs(1)); + + let dev = Device::from_subsystem_sysname("net", "lo").unwrap(); + dev.trigger(DeviceAction::Change).unwrap(); + + /* Sleep more than 3 seconds to wait for the workers being recycled. */ + std::thread::sleep(std::time::Duration::from_secs(4)); + + let mut stream = + std::os::unix::net::UnixStream::connect(CONTROL_MANAGER_LISTEN_ADDR).unwrap(); + stream.write_all(b"exit ").unwrap(); + }); + + run_daemon("none"); + } +} diff --git a/exts/devmaster/src/bin/devctl/main.rs b/exts/devmaster/src/bin/devctl/main.rs index 17645ee6..8f72285e 100644 --- a/exts/devmaster/src/bin/devctl/main.rs +++ b/exts/devmaster/src/bin/devctl/main.rs @@ -18,6 +18,7 @@ mod subcmds; use basic::argv_util::invoked_as; use clap::Parser; use daemon::run_daemon; +use libdevmaster::config::devmaster_conf::DEFAULT_CONFIG; use libdevmaster::framework::control_manager::CONTROL_MANAGER_LISTEN_ADDR; use log::init_log_to_console_syslog; use log::Level; @@ -204,6 +205,12 @@ enum SubCmd { #[clap(short, long)] root: Option, }, + /// + #[clap(display_order = 7)] + Control { + #[clap(short, long)] + exit: bool, + }, } /// subcommand for killing workers @@ -212,10 +219,19 @@ fn subcommand_kill() { stream.write_all(b"kill ").unwrap(); } +/// subcommand for controlling devmaster +fn subcommand_control(exit: bool) { + let mut stream = UnixStream::connect(CONTROL_MANAGER_LISTEN_ADDR).unwrap(); + + if exit { + stream.write_all(b"exit ").unwrap(); + } +} + fn main() -> Result<()> { let argv: Vec = std::env::args().collect(); if invoked_as(argv, "devmaster") { - run_daemon(); + run_daemon(DEFAULT_CONFIG); return Ok(()); } @@ -300,6 +316,7 @@ fn main() -> Result<()> { strict, root, } => subcommand_hwdb(update, test, path, usr, strict, root), + SubCmd::Control { exit } => subcommand_control(exit), } Ok(()) diff --git a/exts/devmaster/src/lib/framework/control_manager.rs b/exts/devmaster/src/lib/framework/control_manager.rs index 342c6a0c..d5a5f440 100644 --- a/exts/devmaster/src/lib/framework/control_manager.rs +++ b/exts/devmaster/src/lib/framework/control_manager.rs @@ -14,7 +14,7 @@ //! use crate::framework::job_queue::JobQueue; use crate::framework::worker_manager::WorkerManager; -use event::Source; +use event::{Events, Source}; use nix::unistd::unlink; use std::os::unix::net::UnixListener; use std::path::Path; @@ -38,7 +38,7 @@ pub struct ControlManager { worker_manager: Weak, /// reference to job queue _job_queue: Weak, - // events: Rc, + events: Rc, } /// public methods @@ -48,6 +48,7 @@ impl ControlManager { listen_addr: String, worker_manager: Rc, job_queue: Rc, + events: Rc, ) -> ControlManager { /* * Cleanup remaining socket if it exists. @@ -67,6 +68,7 @@ impl ControlManager { listener, worker_manager: Rc::downgrade(&worker_manager), _job_queue: Rc::downgrade(&job_queue), + events, } } } @@ -83,6 +85,9 @@ impl ControlManager { "kill" => { self.worker_manager.upgrade().unwrap().kill_workers(); } + "exit" => { + self.events.set_exit(); + } _ => { todo!(); } diff --git a/exts/devmaster/src/lib/framework/devmaster.rs b/exts/devmaster/src/lib/framework/devmaster.rs index 94b93ca0..20735614 100644 --- a/exts/devmaster/src/lib/framework/devmaster.rs +++ b/exts/devmaster/src/lib/framework/devmaster.rs @@ -71,10 +71,10 @@ impl Cache { impl Devmaster { /// generate a devmaster object - pub fn new(events: Rc) -> Rc> { + pub fn new(config_path: &str, events: Rc) -> Rc> { let config = DevmasterConfig::new(); - config.load(DEFAULT_CONFIG); + config.load(config_path); init_log( "devmaster", @@ -127,6 +127,7 @@ impl Devmaster { String::from(CONTROL_MANAGER_LISTEN_ADDR), worker_manager.clone(), job_queue.clone(), + events.clone(), )); let monitor = Rc::new(UeventMonitor::new(job_queue.clone())); let post = Rc::new(GarbageCollect::new(&devmaster)); -- 2.33.0