Skip to content

Commit

Permalink
suricatactl: rust version of suricatactl
Browse files Browse the repository at this point in the history
  • Loading branch information
jasonish committed Sep 6, 2023
1 parent 7353b24 commit c29d2c4
Show file tree
Hide file tree
Showing 9 changed files with 201 additions and 10 deletions.
1 change: 1 addition & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -2591,6 +2591,7 @@ AM_CONDITIONAL([BUILD_SHARED_LIBRARY], [test "x$enable_shared" = "xyes"] && [tes

AC_CONFIG_FILES(Makefile src/Makefile rust/Makefile rust/Cargo.lock rust/Cargo.toml rust/derive/Cargo.toml rust/.cargo/config)
AC_CONFIG_FILES(rust/client/Makefile rust/client/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)
Expand Down
9 changes: 0 additions & 9 deletions python/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ LIBS = \
suricata/sc/suricatasc.py \
suricatasc/__init__.py

BINS = suricatactl

EXTRA_DIST = $(LIBS) bin suricata/config/defaults.py

if HAVE_PYTHON
Expand All @@ -22,20 +20,13 @@ install-exec-local:
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:
Expand Down
7 changes: 6 additions & 1 deletion rust/Cargo.toml.in
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,14 @@ members = [
".",
"derive",
"client",
"suricatactl",
"suricatasc",
]
default-members = [
".",
"suricatactl",
"suricatasc",
]
default-members = [".", "suricatasc"]

[lib]
crate-type = ["staticlib", "rlib"]
Expand Down
4 changes: 4 additions & 0 deletions rust/Makefile.am
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
SUBDIRS = client \
suricatactl \
suricatasc

EXTRA_DIST = src derive \
Expand All @@ -7,6 +8,7 @@ EXTRA_DIST = src derive \
dist/rust-bindings.h \
vendor \
client \
suricatactl \
suricatasc

if !DEBUG
Expand Down Expand Up @@ -60,6 +62,7 @@ endif
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)"
Expand All @@ -68,6 +71,7 @@ install-library:
uninstall-local:
rm -f "$(DESTDIR)$(libdir)/$(RUST_SURICATA_LIBNAME)"
rm -f "$(DESTDIR)$(bindir)/suricatasc"
rm -f "$(DESTDIR)$(bindir)/suricatactl"

clean-local:
rm -rf target
Expand Down
21 changes: 21 additions & 0 deletions rust/suricatactl/Cargo.toml.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
[package]
name = "suricatactl"
version = "@PACKAGE_VERSION@"
edition = "2021"
license = "GPL-2.0-only"

[[bin]]
name = "suricatactl"
path = "@e_rustdir@/suricatactl/src/main.rs"

[dependencies]
regex = "~1.5.5"
env_logger = "0.10.0"
log = "0.4.20"

# 4.0 is the newest version that builds with Rust 1.63.0.
clap = { version = "~4.0.0", features = ["derive"] }

# This dependency is not used directly, but we have to pin this back
# to 0.3.0 for Rust 1.63.0.
clap_lex = "=0.3.0"
1 change: 1 addition & 0 deletions rust/suricatactl/Makefile.am
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
all-local: Cargo.toml
4 changes: 4 additions & 0 deletions rust/suricatactl/src/filestore/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2023 Open Information Security Foundation
// SPDX-License-Identifier: GPL-2.0-only

pub(crate) mod prune;
93 changes: 93 additions & 0 deletions rust/suricatactl/src/filestore/prune.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
// SPDX-FileCopyrightText: Copyright 2023 Open Information Security Foundation
// SPDX-License-Identifier: GPL-2.0-only

use log::{debug, error, info};
use std::path::PathBuf;

use crate::FilestorePruneArgs;

pub(crate) fn prune(args: FilestorePruneArgs) -> Result<(), Box<dyn std::error::Error>> {
let age = parse_age(&args.age).unwrap();
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 {
let metadata = path.metadata()?;
let modified = metadata.modified()?;
let elapsed = modified.elapsed()?;
let elapsed_secs = elapsed.as_secs();
if elapsed_secs > age {
debug!("Deleting {:?}", path);
file_count += 1;
total_bytes += metadata.len();
if !args.dry_run {
if let Err(err) = std::fs::remove_file(&path) {
error!("Failed to delete {}: {}", path.display(), err);
}
}
}
}
}
}

info!("Removed {} files; {} bytes", file_count, total_bytes);

Ok(())
}

/// 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);
}
}
71 changes: 71 additions & 0 deletions rust/suricatactl/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// SPDX-FileCopyrightText: Copyright 2023 Open Information Security Foundation
// SPDX-License-Identifier: GPL-2.0-only

use clap::Parser;
use clap::Subcommand;

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 {
"warn"
} else if cli.verbose > 0 {
"debug"
} else {
"info"
};
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or(log_level)).init();

match cli.command {
Commands::Filestore(filestore) => match filestore.command {
FilestoreCommands::Prune(args) => crate::filestore::prune::prune(args),
},
}
}

0 comments on commit c29d2c4

Please sign in to comment.