AC_CONFIG_FILES(Makefile src/Makefile rust/Makefile rust/Cargo.lock rust/Cargo.toml rust/derive/Cargo.toml rust/.cargo/config.toml)
AC_CONFIG_FILES(rust/sys/Makefile rust/sys/Cargo.toml)
+AC_CONFIG_FILES(rust/suricatactl/Makefile rust/suricatactl/Cargo.toml)
AC_CONFIG_FILES(rust/suricatasc/Makefile rust/suricatasc/Cargo.toml)
AC_CONFIG_FILES(qa/Makefile qa/coccinelle/Makefile)
AC_CONFIG_FILES(rules/Makefile doc/Makefile doc/userguide/Makefile)
suricata/sc/suricatasc.py \
suricatasc/__init__.py
-BINS = suricatactl
-
EXTRA_DIST = $(LIBS) bin suricata/config/defaults.py
if HAVE_PYTHON
install -d -m 0755 "$(DESTDIR)$(prefix)/lib/suricata/python/suricata/ctl"
install -d -m 0755 "$(DESTDIR)$(prefix)/lib/suricata/python/suricata/sc"
install -d -m 0755 "$(DESTDIR)$(prefix)/lib/suricata/python/suricatasc"
- install -d -m 0755 "$(DESTDIR)$(prefix)/bin"
for src in $(LIBS); do \
install -m 0644 $(srcdir)/$$src "$(DESTDIR)$(prefix)/lib/suricata/python/$$src"; \
done
install suricata/config/defaults.py \
"$(DESTDIR)$(prefix)/lib/suricata/python/suricata/config/defaults.py"
- for bin in $(BINS); do \
- cat "$(srcdir)/bin/$$bin" | \
- sed -e "1 s,.*,#"'!'" ${HAVE_PYTHON}," > "${DESTDIR}$(bindir)/$$bin"; \
- chmod 0755 "$(DESTDIR)$(bindir)/$$bin"; \
- done
uninstall-local:
- rm -f $(DESTDIR)$(bindir)/suricatactl
rm -rf $(DESTDIR)$(prefix)/lib/suricata/python
clean-local:
"nom-derive",
]
+[[package]]
+name = "nu-ansi-term"
+version = "0.46.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
+dependencies = [
+ "overload",
+ "winapi",
+]
+
[[package]]
name = "num"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381"
+[[package]]
+name = "overload"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
+
[[package]]
name = "phf"
version = "0.10.1"
"siphasher",
]
+[[package]]
+name = "pin-project-lite"
+version = "0.2.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
+
[[package]]
name = "polyval"
version = "0.5.3"
"digest",
]
+[[package]]
+name = "sharded-slab"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
+dependencies = [
+ "lazy_static",
+]
+
[[package]]
name = "siphasher"
version = "0.3.11"
name = "suricata-sys"
version = "8.0.0-dev"
+[[package]]
+name = "suricatactl"
+version = "8.0.0-dev"
+dependencies = [
+ "clap",
+ "regex",
+ "tracing",
+ "tracing-subscriber",
+]
+
[[package]]
name = "suricatasc"
version = "8.0.0-dev"
"syn 2.0.98",
]
+[[package]]
+name = "thread_local"
+version = "1.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c"
+dependencies = [
+ "cfg-if",
+ "once_cell",
+]
+
[[package]]
name = "time"
version = "0.3.37"
"serde",
]
+[[package]]
+name = "tracing"
+version = "0.1.41"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0"
+dependencies = [
+ "pin-project-lite",
+ "tracing-attributes",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-attributes"
+version = "0.1.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.98",
+]
+
+[[package]]
+name = "tracing-core"
+version = "0.1.33"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c"
+dependencies = [
+ "once_cell",
+ "valuable",
+]
+
+[[package]]
+name = "tracing-log"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"
+dependencies = [
+ "log",
+ "once_cell",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-subscriber"
+version = "0.3.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008"
+dependencies = [
+ "nu-ansi-term",
+ "sharded-slab",
+ "smallvec",
+ "thread_local",
+ "tracing-core",
+ "tracing-log",
+]
+
[[package]]
name = "typenum"
version = "1.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7"
+[[package]]
+name = "valuable"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
+
[[package]]
name = "version_check"
version = "0.9.5"
members = [
".",
"derive",
+ "suricatactl",
"suricatasc",
"sys",
]
default-members = [
".",
+ "suricatactl",
"suricatasc",
]
SUBDIRS = sys \
- suricatasc
+ suricatasc \
+ suricatactl
EXTRA_DIST = src derive \
.cargo/config.toml.in \
derive/Cargo.toml \
sys \
sys/Cargo.toml \
- suricatasc
+ suricatasc \
+ suricatactl
if !DEBUG
RELEASE = --release
install-exec-local:
install -d -m 0755 "$(DESTDIR)$(bindir)"
install -m 0755 $(RUST_SURICATA_LIBDIR)/suricatasc "$(DESTDIR)$(bindir)/suricatasc"
+ install -m 0755 $(RUST_SURICATA_LIBDIR)/suricatactl "$(DESTDIR)$(bindir)/suricatactl"
install-library:
$(MKDIR_P) "$(DESTDIR)$(libdir)"
uninstall-local:
rm -f "$(DESTDIR)$(libdir)/$(RUST_SURICATA_LIBNAME)"
rm -f "$(DESTDIR)$(bindir)/suricatasc"
+ rm -f "$(DESTDIR)$(bindir)/suricatactl"
clean-local:
rm -rf target gen
--- /dev/null
+[package]
+name = "suricatactl"
+version = "@PACKAGE_VERSION@"
+edition = "2021"
+license = "GPL-2.0-only"
+
+[[bin]]
+name = "suricatactl"
+
+[dependencies]
+regex = "~1.5.5"
+tracing = "0.1"
+tracing-subscriber = "0.3"
+
+# 4.0 is the newest version that builds with Rust 1.67.1.
+clap = { version = "=4.2.0", default-features = false, features = ["std", "derive", "help", "usage"] }
--- /dev/null
+EXTRA_DIST = Cargo.toml
+
+all-local: Cargo.toml
--- /dev/null
+// SPDX-FileCopyrightText: Copyright 2023 Open Information Security Foundation
+// SPDX-License-Identifier: GPL-2.0-only
+
+pub(crate) mod prune;
--- /dev/null
+// SPDX-FileCopyrightText: Copyright 2023 Open Information Security Foundation
+// SPDX-License-Identifier: GPL-2.0-only
+
+use std::path::{Path, PathBuf};
+use tracing::{debug, error, info};
+
+use crate::FilestorePruneArgs;
+
+pub(crate) fn prune(args: FilestorePruneArgs) -> Result<(), Box<dyn std::error::Error>> {
+ let age = parse_age(&args.age)?;
+ info!("Pruning files older than {} seconds", age);
+
+ let mut total_bytes = 0;
+ let mut file_count = 0;
+
+ let mut stack = vec![PathBuf::from(&args.directory)];
+ while let Some(dir) = stack.pop() {
+ for entry in std::fs::read_dir(dir)? {
+ let path = entry?.path();
+ if path.is_dir() {
+ stack.push(path);
+ } else {
+ match FileInfo::from_path(&path) {
+ Ok(info) => {
+ if info.age > age {
+ debug!("Deleting {:?}", path);
+ file_count += 1;
+ total_bytes += info.size;
+ if !args.dry_run {
+ if let Err(err) = std::fs::remove_file(&path) {
+ error!("Failed to delete {}: {}", path.display(), err);
+ }
+ }
+ }
+ }
+ Err(err) => {
+ error!(
+ "Failed to get last modified time of file {}: {}",
+ path.display(),
+ err
+ );
+ }
+ }
+ }
+ }
+ }
+
+ info!("Removed {} files; {} bytes", file_count, total_bytes);
+
+ Ok(())
+}
+
+struct FileInfo {
+ age: u64,
+ size: u64,
+}
+
+impl FileInfo {
+ fn from_path(path: &Path) -> Result<Self, Box<dyn std::error::Error>> {
+ let metadata = path.metadata()?;
+ let age = metadata.modified()?.elapsed()?.as_secs();
+ Ok(Self {
+ age,
+ size: metadata.len(),
+ })
+ }
+}
+
+/// Given input like "1s", "1m", "1h" or "1d" return the number of
+/// seconds
+fn parse_age(age: &str) -> Result<u64, String> {
+ // Use a regex to separate the value from the unit.
+ let re = regex::Regex::new(r"^(\d+)([smhd])$").unwrap();
+ let caps = re.captures(age).ok_or_else(|| {
+ format!(
+ "Invalid age: {}. Must be a number followed by one of s, m, h, d",
+ age
+ )
+ })?;
+ let value = caps
+ .get(1)
+ .unwrap()
+ .as_str()
+ .parse::<u64>()
+ .map_err(|e| format!("Invalid age: {}: {}", age, e))?;
+ let unit = caps.get(2).unwrap().as_str();
+
+ match unit {
+ "s" => Ok(value),
+ "m" => Ok(value * 60),
+ "h" => Ok(value * 60 * 60),
+ "d" => Ok(value * 60 * 60 * 24),
+ _ => unreachable!(),
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+
+ #[test]
+ fn test_parse_age() {
+ assert!(parse_age("1").is_err());
+ assert!(parse_age("s").is_err());
+ assert!(parse_age("1a").is_err());
+
+ // Valid tests
+ assert_eq!(parse_age("1s").unwrap(), 1);
+ assert_eq!(parse_age("3s").unwrap(), 3);
+ assert_eq!(parse_age("1m").unwrap(), 60);
+ assert_eq!(parse_age("3m").unwrap(), 180);
+ assert_eq!(parse_age("3h").unwrap(), 10800);
+ assert_eq!(parse_age("1d").unwrap(), 86400);
+ assert_eq!(parse_age("3d").unwrap(), 86400 * 3);
+ }
+}
--- /dev/null
+// SPDX-FileCopyrightText: Copyright 2023 Open Information Security Foundation
+// SPDX-License-Identifier: GPL-2.0-only
+
+use clap::Parser;
+use clap::Subcommand;
+use tracing::Level;
+
+mod filestore;
+
+#[derive(Parser, Debug)]
+struct Cli {
+ #[arg(long, short, global = true, action = clap::ArgAction::Count)]
+ verbose: u8,
+
+ #[arg(
+ long,
+ short,
+ global = true,
+ help = "Quiet mode, only warnings and errors will be logged"
+ )]
+ quiet: bool,
+
+ #[command(subcommand)]
+ command: Commands,
+}
+
+#[derive(Subcommand, Debug)]
+enum Commands {
+ /// Filestore management commands
+ Filestore(FilestoreCommand),
+}
+
+#[derive(Parser, Debug)]
+struct FilestoreCommand {
+ #[command(subcommand)]
+ command: FilestoreCommands,
+}
+
+#[derive(Subcommand, Debug)]
+enum FilestoreCommands {
+ /// Remove files by age
+ Prune(FilestorePruneArgs),
+}
+
+#[derive(Parser, Debug)]
+struct FilestorePruneArgs {
+ #[arg(long, short = 'n', help = "only print what would happen")]
+ dry_run: bool,
+ #[arg(long, short, help = "file-store directory")]
+ directory: String,
+ #[arg(long, help = "prune files older than age, units: s, m, h, d")]
+ age: String,
+}
+
+fn main() -> Result<(), Box<dyn std::error::Error>> {
+ let cli = Cli::parse();
+
+ let log_level = if cli.quiet {
+ Level::WARN
+ } else if cli.verbose > 0 {
+ Level::DEBUG
+ } else {
+ Level::INFO
+ };
+ tracing_subscriber::fmt().with_max_level(log_level).init();
+
+ match cli.command {
+ Commands::Filestore(filestore) => match filestore.command {
+ FilestoreCommands::Prune(args) => crate::filestore::prune::prune(args),
+ },
+ }
+}