diff --git a/README.md b/README.md
index a75c282..f51825a 100644
--- a/README.md
+++ b/README.md
@@ -49,6 +49,7 @@ The entire configuration is defined in a YAML file, typically named `suriconf.ya
|-----------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `suri-configuration` | Path to the default Suricata configuration file. |
| `log-dir` | Directory for Suricata logs (requires read/write permissions). |
+| `socket` | Path to Suricata socket. |
| `preconf-time` | Duration of the Suricata preconfiguration run. |
| `analysis` | Analysis type: `dynamic` (multiple Suricata runs) or `static` (single Suricata run). |
| `mode` | Output mode: `suggestion` (recommendations only) or `modify` (writes changes to Suricata configuration file).
Modify mode with `yaml_change`: `ask` (user confirms each change) or `force` (all detected changes are applied automatically). |
diff --git a/src/argument.rs b/src/argument.rs
index 1ce4f80..d3caebe 100644
--- a/src/argument.rs
+++ b/src/argument.rs
@@ -68,6 +68,10 @@ pub enum Commands {
#[clap(short='l', long)]
path_to_logs: Option,
+ /// Change path to Unix Socket
+ #[clap(short='s', long)]
+ path_to_socket: Option,
+
/// Change the time of Suricata preconfiguration run (in seconds)
#[clap(short='t', long="time")]
preconf_time: Option,
diff --git a/src/suricata.rs b/src/suricata.rs
index a18235d..2ce4b46 100644
--- a/src/suricata.rs
+++ b/src/suricata.rs
@@ -6,6 +6,8 @@ SPDX-License-Identifier: BSD-3-Clause
This file executes Suricata.
*/
+const PIDFILE: &str = "/var/run/suriconf_suricata.pid";
+
use std::process::{Child, Command};
use crate::yaml::{emergency_check_memcap, Suriconf};
use crate::{FLOW_WINDOW, MIN_RUN};
@@ -16,10 +18,11 @@ use std::time::Duration;
use crossbeam_channel::{bounded, select, tick, Receiver};
use std::process::Stdio;
use std::io::{BufRead, BufReader};
-use std::thread;
-use procfs::process::{all_processes, Process};
+use std::{fs, thread};
+use procfs::process::{Process};
use signal_hook::consts::SIGINT;
use signal_hook::iterator::Signals;
+use std::path::{Path, PathBuf};
use std::sync::{Arc, Mutex};
use std::sync::atomic::{AtomicBool, Ordering};
@@ -35,6 +38,14 @@ pub fn execute_suricata<'a>(suriconf: &Suriconf, logs: &mut CreatedLogs, options
let mut suricata_again = SuricataAgain::default();
let mut vec_of_sur_cmd: Vec = vec![];
+ match delete_pid_file() {
+ Err(e) => {
+ panic!("{e}");
+ }
+ Ok(()) => {}
+ };
+
+ set_pid_file(&mut vec_of_sur_cmd);
get_capture_mode(suriconf, &mut vec_of_sur_cmd);
if cfg!(target_os = "windows") {
@@ -66,7 +77,7 @@ pub fn execute_suricata<'a>(suriconf: &Suriconf, logs: &mut CreatedLogs, options
let mut child = Command::new("sudo")
.arg("-n")
.args(args)
- .stdout(Stdio::piped())
+ .stdout(Stdio::null())
.stderr(Stdio::piped())
.spawn()
.expect("Failed to execute process.");
@@ -81,6 +92,14 @@ pub fn execute_suricata<'a>(suriconf: &Suriconf, logs: &mut CreatedLogs, options
}
});
+ let suri_pid = match wait_on_suricata_start(&suriconf.socket, &mut child) {
+ Ok(pid) => pid,
+ Err(e) => {
+ kill_suricata(&mut child);
+ panic!("{e}")
+ }
+ };
+
let timeout = Duration::from_secs(suriconf.preconf_time);
let start = std::time::Instant::now();
let ticks_thread1 = tick(Duration::from_millis(100));
@@ -140,7 +159,6 @@ pub fn execute_suricata<'a>(suriconf: &Suriconf, logs: &mut CreatedLogs, options
let ticks = tick(Duration::from_millis(100));
let emergency_ticks = tick(Duration::from_secs(FLOW_WINDOW));
- let suri_pid = check_process_name_for_suricata_main().expect("Unable to get Suricata-Main.");
loop {
select! {
@@ -198,6 +216,26 @@ pub fn get_capture_mode(suriconf: &Suriconf, vec_of_sur_cmd: &mut Vec) {
});
}
+pub fn delete_pid_file() -> std::io::Result<()> {
+ let status = Command::new("sudo")
+ .arg("-n")
+ .arg("rm")
+ .arg("-f")
+ .arg(PIDFILE)
+ .status()?;
+
+ if status.success() {
+ Ok(())
+ } else {
+ Err(std::io::Error::other("Failed to delete Suricata PID file."))
+ }
+}
+
+pub fn set_pid_file(vec_of_sur_cmd: &mut Vec) {
+ vec_of_sur_cmd.push("--pidfile".to_string());
+ vec_of_sur_cmd.push(PIDFILE.to_string());
+}
+
pub fn get_cpu_usage(sys: &mut SystemVar) {
sys.sys.refresh_cpu_usage();
@@ -272,24 +310,53 @@ pub fn get_workers(sys: &mut SystemVar) -> u64 {
}
workers
}
-
-fn check_process_name_for_suricata_main() -> Option {
- for _ in 0..10 {
- for prc in all_processes().expect("Unable to get all processes.") {
- let process: Process;
- match prc {
- Ok(prc) => {process = prc}
- Err(_) => {continue}
+fn get_suricata_pid() -> Result {
+ for _ in 0..20 {
+ if let Ok(content) = fs::read_to_string(PIDFILE) {
+ if let Ok(pid) = content.trim().parse::() {
+ if let Ok(comm) = fs::read_to_string(format!("/proc/{}/comm", pid)) {
+ if comm.trim() == "Suricata-Main" {
+ return Ok(pid);
+ }
+ }
}
+ }
+ thread::sleep(Duration::from_millis(1000));
+ }
+ Err("Suricata process not found.".into())
+}
+fn wait_on_suricata_start(socket: &PathBuf, child: &mut Child) -> Result {
+ match get_suricata_pid() {
+ Ok(pid) => {
+ let socket = socket.to_str().ok_or("Socket path is not valid UTF-8.")?;
+ for _ in 0..120 {
+ let output = Command::new("sudo")
+ .arg("-n")
+ .arg("suricatasc")
+ .arg("-c")
+ .arg("uptime")
+ .arg(socket)
+ .output()
+ .map_err(|e| format!("Unable to execute Suricata socket control tool: {e}"))?;
+
+ if output.status.success() {
+ return Ok(pid)
+ }
- if process.stat().expect("Unable to find stats about process.").comm == "Suricata-Main" {
- return Some(process.pid);
+ if let Some(status) = child.try_wait().expect("Unable to get Suricata status.") {
+ return Err(format!("Suricata exited during startup with status: {status}."));
+ }
+
+ thread::sleep(Duration::from_millis(1000));
}
+ Err("Suricata could not start.".into())
+ }
+ Err(e) => {
+ Err(e)
}
- thread::sleep(Duration::from_millis(100));
}
- None
}
+
fn get_cores_with_threads(suri_pid: i32, sys: &mut SystemVar) {
let proc = Process::new(suri_pid).expect("Unable to create process.");
let tasks = proc.tasks().expect("Unable to get process tasks.");
@@ -330,6 +397,7 @@ pub fn kill_suricata(child: &mut Child) {
.arg("Suricata-Main")
.status()
.expect("Unable to pkill Suricata-Main (SIGKILL).");
+
if !output.success() {
panic!("Process failed.");
}
@@ -356,4 +424,4 @@ pub fn check_min_suricata_runtime_for_modules(suriconf: &Suriconf) {
panic!("Unable to execute Suricata and have enough samples from preconfiguration, \
FlowThreads module needs at least 6 minutes.")
}
-}
\ No newline at end of file
+}
diff --git a/src/suriconf.yaml b/src/suriconf.yaml
index 2837c83..07131d2 100644
--- a/src/suriconf.yaml
+++ b/src/suriconf.yaml
@@ -9,6 +9,7 @@ ethtool-bin: /usr/bin/ethtool
ifconfig-bin: /usr/sbin/ifconfig
ip-bin: /usr/sbin/ip
log-dir: /var/log/suricata/
+socket: /var/run/suricata/suricata-command.socket
preconf-time: 360 # in seconds
# 360 is minimum for flow_threads module
analysis: static # static/dynamic
diff --git a/src/yaml.rs b/src/yaml.rs
index d39d6d7..cd63077 100644
--- a/src/yaml.rs
+++ b/src/yaml.rs
@@ -726,6 +726,7 @@ pub struct Suriconf {
pub ifconfig_bin: PathBuf,
pub ip_bin: PathBuf,
pub log_dir: PathBuf,
+ pub socket: PathBuf,
pub preconf_time: u64,
pub analysis: Analysis,
pub mode: Mode,
@@ -844,6 +845,13 @@ impl Suriconf {
self.find_log_dir(suriconf_string).expect("Unable to parse path to logs.")
};
+ self.socket = if let Some(Commands::Suricata { path_to_socket: Some(p), .. }) = &args.cmd {
+ p.clone()
+ }
+ else {
+ self.find_socket(suriconf_string).expect("Unable to parse path to unix socket.")
+ };
+
self.preconf_time = if let Some( Commands::Suricata { preconf_time: Some(p), .. }) = &args.cmd {
p.clone()
}
@@ -952,6 +960,10 @@ impl Suriconf {
text.get("log-dir").and_then(|c| c.as_str()).map(|c| PathBuf::from(c))
}
+ pub fn find_socket(&self, text: &Value) -> Option {
+ text.get("socket").and_then(|c| c.as_str()).map(|c| PathBuf::from(c))
+ }
+
pub fn find_preconf_time(&self, text: &Value) -> Option {
text.get("preconf-time").and_then(|t| t.as_u64())
}