From 714eeceecbe09fd8855cd84279dc13e45a093fb2 Mon Sep 17 00:00:00 2001
From: chenjiayi <chenjiayi22@huawei.com>
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<String>,
     },
+    ///
+    #[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<String> = 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<WorkerManager>,
     /// reference to job queue
     _job_queue: Weak<JobQueue>,
-    // events: Rc<Events>,
+    events: Rc<Events>,
 }
 
 /// public methods
@@ -48,6 +48,7 @@ impl ControlManager {
         listen_addr: String,
         worker_manager: Rc<WorkerManager>,
         job_queue: Rc<JobQueue>,
+        events: Rc<Events>,
     ) -> 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<Events>) -> Rc<RefCell<Devmaster>> {
+    pub fn new(config_path: &str, events: Rc<Events>) -> Rc<RefCell<Devmaster>> {
         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

