Update clap to remove suppressions (#2856)

A Friday afternoon jaunt.

To be merged after #2855; does the remaining work to close #2277 (and closes off some things that Dependabot was trying to upgrade).

Tested by manually running the commands; we don't have good coverage for this kind of stuff. OTOH, most of these commands are for the experimental local fuzzing mode, which is not fully supported yet. I did specifically test the `onefuzz-task managed` command which is the one used in production.

## Details

- Bump `clap` to 4.1.6
  - Remove `structopt` as this is subsumed by clap now
- Bump `envlogger` to 0.10 (removes problematic dependency)
- Set `default-features=false` on `proc-maps` (removes a feature which is only needed to support FreeBSD), and bump it to 0.3

The main changes migrating `clap` are:

- `value_t!` is gone; now use `matches.get_one::<T>`. If `T` is not `String` then a parser must have been registered on the `Arg` when it was created, with `arg.value_parser(value_parser!(T))`.
- `Command::with_name` and `Arg::with_name` are now called `new`.
- `Command` and `Subcommand` were unified, and `App` is removed.
- `arg.takes_value(true)` is gone; it is the default. For flags use `arg.action(ArgAction::SetTrue)` and then retrieve the flag value with `matches.get_flag`.

This code would be simplified a lot by using the `clap::Parser` on structs, but that requires reworking the code significantly as we cannot dynamically add/remove arguments the way that this is currently done.

## Also found

Found one bug while manually testing the `onefuzz-task local` commands; see comment below.
This commit is contained in:
George Pollard 2023-02-20 10:45:15 +13:00 committed by GitHub
parent 1b07b7df25
commit cd18c6066d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 642 additions and 806 deletions

300
src/agent/Cargo.lock generated
View File

@ -32,15 +32,6 @@ dependencies = [
"memchr",
]
[[package]]
name = "ansi_term"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
dependencies = [
"winapi",
]
[[package]]
name = "anyhow"
version = "1.0.51"
@ -122,17 +113,6 @@ dependencies = [
"log",
]
[[package]]
name = "atty"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [
"hermit-abi 0.1.19",
"libc",
"winapi",
]
[[package]]
name = "autocfg"
version = "1.1.0"
@ -266,25 +246,21 @@ dependencies = [
[[package]]
name = "bindgen"
version = "0.59.1"
version = "0.59.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "453c49e5950bb0eb63bb3df640e31618846c89d5b7faa54040d76e98e0134375"
checksum = "2bd2a9a458e8f4304c52c43ebb0cfbd520289f8379a52e329a38afda99bf8eb8"
dependencies = [
"bitflags",
"cexpr",
"clang-sys",
"clap 2.34.0",
"env_logger 0.8.4",
"lazy_static",
"lazycell",
"log",
"peeking_take_while",
"proc-macro2 1.0.47",
"quote 1.0.9",
"regex",
"rustc-hash",
"shlex",
"which",
]
[[package]]
@ -293,28 +269,16 @@ version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitvec"
version = "0.19.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55f93d0ef3363c364d5976646a38f04cf67cfe1d4c8d160cdea02cab2c116b33"
dependencies = [
"funty 1.1.0",
"radium 0.5.3",
"tap",
"wyz 0.2.0",
]
[[package]]
name = "bitvec"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1489fcb93a5bb47da0462ca93ad252ad6af2145cce58d10d46a83931ba9f016b"
dependencies = [
"funty 2.0.0",
"radium 0.7.0",
"funty",
"radium",
"tap",
"wyz 0.5.0",
"wyz",
]
[[package]]
@ -376,11 +340,11 @@ checksum = "d26a6ce4b6a484fa3edb70f7efa6fc430fd2b87285fe8b84304fd0936faa0dc0"
[[package]]
name = "cexpr"
version = "0.5.0"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db507a7679252d2276ed0dd8113c6875ec56d3089f9225b2b42c30cc1f8e5c89"
checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766"
dependencies = [
"nom 6.1.2",
"nom 7.1.0",
]
[[package]]
@ -421,86 +385,32 @@ dependencies = [
[[package]]
name = "clap"
version = "2.34.0"
version = "4.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c"
checksum = "ec0b0588d44d4d63a87dbd75c136c166bbfd9a86a31cb89e09906521c7d3f5e3"
dependencies = [
"ansi_term",
"atty",
"bitflags",
"strsim 0.8.0",
"textwrap 0.11.0",
"unicode-width",
"vec_map",
]
[[package]]
name = "clap"
version = "3.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d20de3739b4fb45a17837824f40aa1769cc7655d7a83e68739a77fe7b30c87a"
dependencies = [
"atty",
"bitflags",
"clap_derive 3.2.4",
"clap_lex 0.2.2",
"indexmap",
"clap_derive",
"clap_lex",
"is-terminal",
"once_cell",
"strsim 0.10.0",
"termcolor",
"textwrap 0.15.0",
]
[[package]]
name = "clap"
version = "4.0.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2148adefda54e14492fb9bddcc600b4344c5d1a3123bd666dcb939c6f0e0e57e"
dependencies = [
"atty",
"bitflags",
"clap_derive 4.0.21",
"clap_lex 0.3.0",
"once_cell",
"strsim 0.10.0",
"strsim",
"termcolor",
]
[[package]]
name = "clap_derive"
version = "3.2.4"
version = "4.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "026baf08b89ffbd332836002ec9378ef0e69648cbfadd68af7cd398ca5bf98f7"
checksum = "684a277d672e91966334af371f1a7b5833f9aa00b07c84e92fbce95e00208ce8"
dependencies = [
"heck 0.4.0",
"heck",
"proc-macro-error",
"proc-macro2 1.0.47",
"quote 1.0.9",
"syn 1.0.103",
]
[[package]]
name = "clap_derive"
version = "4.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0177313f9f02afc995627906bbd8967e2be069f5261954222dac78290c2b9014"
dependencies = [
"heck 0.4.0",
"proc-macro-error",
"proc-macro2 1.0.47",
"quote 1.0.9",
"syn 1.0.103",
]
[[package]]
name = "clap_lex"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5538cd660450ebeb4234cfecf8f2284b844ffc4c50531e66d584ad5b91293613"
dependencies = [
"os_str_bytes",
]
[[package]]
name = "clap_lex"
version = "0.3.0"
@ -559,11 +469,11 @@ name = "coverage"
version = "0.1.0"
dependencies = [
"anyhow",
"clap 4.0.26",
"clap",
"cobertura",
"debuggable-module",
"debugger",
"env_logger 0.10.0",
"env_logger",
"iced-x86",
"log",
"pete",
@ -753,7 +663,7 @@ name = "debuggable-module"
version = "0.1.0"
dependencies = [
"anyhow",
"clap 4.0.26",
"clap",
"elsa",
"gimli",
"goblin 0.6.0",
@ -853,10 +763,10 @@ name = "dynamic-library"
version = "0.1.0"
dependencies = [
"anyhow",
"clap",
"debugger",
"lazy_static",
"regex",
"structopt",
"thiserror",
"winapi",
"winreg 0.10.1",
@ -895,32 +805,6 @@ dependencies = [
"cfg-if 1.0.0",
]
[[package]]
name = "env_logger"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a19187fea3ac7e84da7dacf48de0c45d63c6a76f9490dae389aead16c243fce3"
dependencies = [
"atty",
"humantime",
"log",
"regex",
"termcolor",
]
[[package]]
name = "env_logger"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b2cf0344971ee6c64c31be0d530793fba457d322dfec2810c453d0ef228f9c3"
dependencies = [
"atty",
"humantime",
"log",
"regex",
"termcolor",
]
[[package]]
name = "env_logger"
version = "0.10.0"
@ -1078,12 +962,6 @@ dependencies = [
"libc",
]
[[package]]
name = "funty"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fed34cd105917e91daa4da6b3728c47b068749d6a62c59811f06ed2ac71d9da7"
[[package]]
name = "funty"
version = "2.0.0"
@ -1304,30 +1182,12 @@ version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
[[package]]
name = "heck"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c"
dependencies = [
"unicode-segmentation",
]
[[package]]
name = "heck"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
[[package]]
name = "hermit-abi"
version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
dependencies = [
"libc",
]
[[package]]
name = "hermit-abi"
version = "0.2.6"
@ -1581,7 +1441,7 @@ version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28dfb6c8100ccc63462345b67d1bbc3679177c75ee4bf59bf29c8b1d110b8189"
dependencies = [
"hermit-abi 0.2.6",
"hermit-abi",
"io-lifetimes",
"rustix",
"windows-sys 0.42.0",
@ -1692,10 +1552,11 @@ dependencies = [
[[package]]
name = "libproc"
version = "0.10.0"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6466fc1f834276563fbbd4be1c24236ef92bb9efdbd4691e07f1cf85a0b407f0"
checksum = "0b799ad155d75ce914c467ee5627b62247c20d4aedbd446f821484cebf3cded7"
dependencies = [
"bindgen",
"errno",
"libc",
]
@ -1914,18 +1775,6 @@ dependencies = [
"version_check",
]
[[package]]
name = "nom"
version = "6.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7413f999671bd4745a7b624bd370a569fb6bc574b23c83a3c5ed2e453f3d5e2"
dependencies = [
"bitvec 0.19.6",
"funty 1.1.0",
"memchr",
"version_check",
]
[[package]]
name = "nom"
version = "7.1.0"
@ -2002,7 +1851,7 @@ version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b"
dependencies = [
"hermit-abi 0.2.6",
"hermit-abi",
"libc",
]
@ -2039,6 +1888,7 @@ dependencies = [
"backoff",
"base64",
"bytes",
"clap",
"cpp_demangle 0.3.5",
"debugger",
"dunce",
@ -2067,7 +1917,6 @@ dependencies = [
"sha2",
"stacktrace-parser",
"storage-queue",
"structopt",
"strum",
"strum_macros",
"tempfile",
@ -2089,9 +1938,9 @@ dependencies = [
"anyhow",
"async-trait",
"backtrace",
"clap 3.2.4",
"clap",
"downcast-rs",
"env_logger 0.9.0",
"env_logger",
"futures",
"log",
"nix",
@ -2135,11 +1984,11 @@ dependencies = [
"azure_storage_blobs",
"backoff",
"chrono",
"clap 2.34.0",
"clap",
"cobertura",
"coverage",
"crossterm 0.22.1",
"env_logger 0.9.0",
"env_logger",
"flume",
"futures",
"hex",
@ -2514,12 +2363,11 @@ dependencies = [
[[package]]
name = "proc-maps"
version = "0.2.1"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2f62c16ccc63ce2f590b17b8b9b33616f59631b8982ad52ed21e7f6d936c409"
checksum = "2c790484f98e8b00e2385ebde989077698f99417307a74361001ada102f8c2ce"
dependencies = [
"anyhow",
"bindgen",
"libc",
"libproc",
"mach2",
@ -2588,12 +2436,6 @@ dependencies = [
"proc-macro2 1.0.47",
]
[[package]]
name = "radium"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "941ba9d78d8e2f7ce474c015eea4d9c6d25b6a3327f9832ee29a4de27f91bbb8"
[[package]]
name = "radium"
version = "0.7.0"
@ -3102,7 +2944,7 @@ version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "410b26ed97440d90ced3e2488c868d56a86e2064f5d7d6f417909b286afe25e5"
dependencies = [
"heck 0.4.0",
"heck",
"proc-macro2 1.0.47",
"quote 1.0.9",
"syn 1.0.103",
@ -3132,14 +2974,14 @@ name = "srcview"
version = "0.1.2"
dependencies = [
"anyhow",
"env_logger 0.9.0",
"clap",
"env_logger",
"log",
"nom 7.1.0",
"pdb",
"quick-xml",
"regex",
"serde",
"structopt",
]
[[package]]
@ -3207,42 +3049,12 @@ dependencies = [
"serde",
]
[[package]]
name = "strsim"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
[[package]]
name = "strsim"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
name = "structopt"
version = "0.3.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c6b5c64445ba8094a6ab0c3cd2ad323e07171012d9c98b0b15651daf1787a10"
dependencies = [
"clap 2.34.0",
"lazy_static",
"structopt-derive",
]
[[package]]
name = "structopt-derive"
version = "0.4.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0"
dependencies = [
"heck 0.3.3",
"proc-macro-error",
"proc-macro2 1.0.47",
"quote 1.0.9",
"syn 1.0.103",
]
[[package]]
name = "strum"
version = "0.24.0"
@ -3255,7 +3067,7 @@ version = "0.24.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6878079b17446e4d3eba6192bb0a2950d5b14f0ed8424b852310e5a94345d0ef"
dependencies = [
"heck 0.4.0",
"heck",
"proc-macro2 1.0.47",
"quote 1.0.9",
"rustversion",
@ -3298,7 +3110,7 @@ version = "10.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f94766a96b5834eaf72f9cb99a5a45e63fa44f1084705b705d9d31bb6455434"
dependencies = [
"bitvec 1.0.0",
"bitvec",
"dmsort",
"elementtree",
"elsa",
@ -3415,21 +3227,6 @@ dependencies = [
"winapi-util",
]
[[package]]
name = "textwrap"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
dependencies = [
"unicode-width",
]
[[package]]
name = "textwrap"
version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb"
[[package]]
name = "thiserror"
version = "1.0.37"
@ -3753,12 +3550,6 @@ version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
[[package]]
name = "vec_map"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
[[package]]
name = "version_check"
version = "0.9.3"
@ -3905,15 +3696,6 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "which"
version = "3.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d011071ae14a2f6671d0b74080ae0cd8ebf3a6f8c9589a2cd45f23126fe29724"
dependencies = [
"libc",
]
[[package]]
name = "win-util"
version = "0.1.0"
@ -4119,12 +3901,6 @@ dependencies = [
"tokio",
]
[[package]]
name = "wyz"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85e60b0d1b5f99db2556934e21937020776a5d31520bf169e851ac44e6420214"
[[package]]
name = "wyz"
version = "0.5.0"

View File

@ -6,9 +6,9 @@ license = "MIT"
[dependencies]
anyhow = "1.0"
clap = { version = "4.1.6", features = ["derive"] }
lazy_static = "1.4"
regex = "1.6"
structopt = "0.3"
thiserror = "1.0"
[target.'cfg(windows)'.dependencies]

View File

@ -4,22 +4,22 @@
use std::process::{Command, Stdio};
use anyhow::Result;
use structopt::StructOpt;
use clap::Parser;
#[derive(Debug, StructOpt)]
#[derive(Parser, Debug)]
struct Opt {
#[structopt(min_values = 1)]
#[arg(required = true, num_args = 1..)]
argv: Vec<String>,
#[structopt(short, long)]
#[arg(short, long)]
quiet: bool,
#[structopt(short, long)]
#[arg(short, long)]
ld_library_path: Option<String>,
}
fn main() -> Result<()> {
let opt = Opt::from_args();
let opt = Opt::parse();
let exe = &opt.argv[0];
let mut cmd = Command::new(exe);

View File

@ -10,7 +10,7 @@ license = "MIT"
anyhow = { version = "1.0", features = ["backtrace"] }
async-trait = "0.1"
downcast-rs = "1.2"
env_logger = "0.9"
env_logger = "0.10"
futures = "0.3"
log = "0.4"
onefuzz = { path = "../onefuzz" }
@ -25,7 +25,7 @@ storage-queue = { path = "../storage-queue" }
tokio = { version = "1.24", features = ["full"] }
url = { version = "2.3", features = ["serde"] }
uuid = { version = "0.8", features = ["serde", "v4"] }
clap = { version = "3.2.4", features = ["derive", "cargo"] }
clap = { version = "4", features = ["derive", "cargo"] }
reqwest-retry = { path = "../reqwest-retry" }
onefuzz-telemetry = { path = "../onefuzz-telemetry" }
backtrace = "0.3"

View File

@ -54,20 +54,21 @@ enum Opt {
#[derive(Parser, Debug)]
struct RunOpt {
#[clap(short, long = "--config", parse(from_os_str))]
#[arg(short, long = "config")]
config_path: Option<PathBuf>,
/// re-executes as a child process, recording stdout/stderr to files in
/// the specified directory
#[clap(short, long = "--redirect-output", parse(from_os_str))]
#[arg(short, long = "redirect-output")]
redirect_output: Option<PathBuf>,
#[clap(long = "--machine_id")]
#[arg(long = "machine_id")]
machine_id: Option<Uuid>,
#[clap(long = "--machine_name")]
#[arg(long = "machine_name")]
machine_name: Option<String>,
#[clap(long = "--reset_lock", takes_value = false, action = ArgAction::SetTrue )]
#[arg(long = "reset_lock", action = ArgAction::SetTrue )]
reset_node_lock: bool,
}

View File

@ -15,11 +15,11 @@ arraydeque = "0.5"
async-trait = "0.1"
atexit = { path = "../atexit" }
backoff = { version = "0.4", features = ["tokio"] }
clap = "2.34"
clap = { version = "4", features = ["cargo", "string"] }
cobertura = { path = "../cobertura" }
coverage = { path = "../coverage" }
crossterm = "0.22"
env_logger = "0.9"
env_logger = "0.10"
flume = "0.10"
futures = "0.3"
hex = "0.4"

View File

@ -9,7 +9,7 @@ use crate::local::{
libfuzzer_test_input, radamsa, test_input, tui::TerminalUi,
};
use anyhow::{Context, Result};
use clap::{App, Arg, SubCommand};
use clap::{Arg, ArgAction, Command};
use std::str::FromStr;
use std::time::Duration;
use strum::IntoEnumIterator;
@ -37,17 +37,22 @@ enum Commands {
const TIMEOUT: &str = "timeout";
const TUI: &str = "tui";
pub async fn run(args: clap::ArgMatches<'static>) -> Result<()> {
let running_duration = value_t!(args, TIMEOUT, u64).ok();
let start_ui = args.is_present(TUI);
pub async fn run(args: clap::ArgMatches) -> Result<()> {
let running_duration = args.get_one::<u64>(TIMEOUT).copied();
let (cmd, sub_args) = args.subcommand();
let command =
Commands::from_str(cmd).with_context(|| format!("unexpected subcommand: {cmd}"))?;
let start_ui = args.get_flag(TUI);
let sub_args = sub_args
.ok_or_else(|| anyhow!("missing subcommand arguments"))?
.to_owned();
let (cmd, sub_args) = args.subcommand().ok_or_else(|| {
format_err!(
"Expected subcommand for 'local'. Use 'local help' to see available subcommands."
)
})?;
let command = Commands::from_str(cmd).with_context(|| {
format!("Unexpected subcommand: {cmd}. Use 'local help' to see available subcommands.")
})?;
let sub_args = sub_args.clone();
let terminal = if start_ui {
Some(TerminalUi::init()?)
@ -104,20 +109,20 @@ pub async fn run(args: clap::ArgMatches<'static>) -> Result<()> {
}
}
pub fn args(name: &str) -> App<'static, 'static> {
let mut cmd = SubCommand::with_name(name)
pub fn args(name: &'static str) -> Command {
let mut cmd = Command::new(name)
.about("pre-release local fuzzing")
.arg(
Arg::with_name(TIMEOUT)
Arg::new(TIMEOUT)
.long(TIMEOUT)
.help("The maximum running time in seconds")
.takes_value(true),
.value_parser(value_parser!(u64))
.help("The maximum running time in seconds"),
)
.arg(
Arg::with_name(TUI)
Arg::new(TUI)
.long(TUI)
.help("Enable the terminal UI")
.takes_value(false),
.action(ArgAction::SetTrue),
);
for subcommand in Commands::iter() {

View File

@ -7,7 +7,7 @@ use std::{
use anyhow::Result;
use backoff::{future::retry, Error as BackoffError, ExponentialBackoff};
use clap::{App, Arg, ArgMatches};
use clap::{Arg, ArgAction, ArgMatches, Command};
use flume::Sender;
use onefuzz::{
blob::url::BlobContainerUrl, machine_id::MachineIdentity, monitor::DirectoryMonitor,
@ -76,40 +76,41 @@ pub struct LocalContext {
pub event_sender: Option<Sender<UiEvent>>,
}
pub fn get_hash_map(args: &clap::ArgMatches<'_>, name: &str) -> Result<HashMap<String, String>> {
pub fn get_hash_map(args: &clap::ArgMatches, name: &str) -> Result<HashMap<String, String>> {
let mut env = HashMap::new();
for opt in args.values_of_lossy(name).unwrap_or_default() {
for opt in args.get_many::<String>(name).unwrap_or_default() {
let (k, v) = parse_key_value(opt)?;
env.insert(k, v);
}
Ok(env)
}
pub fn get_cmd_exe(cmd_type: CmdType, args: &clap::ArgMatches<'_>) -> Result<String> {
pub fn get_cmd_exe(cmd_type: CmdType, args: &clap::ArgMatches) -> Result<String> {
let name = match cmd_type {
CmdType::Target => TARGET_EXE,
// CmdType::Supervisor => SUPERVISOR_EXE,
CmdType::Generator => GENERATOR_EXE,
};
let exe = value_t!(args, name, String)?;
Ok(exe)
args.get_one::<String>(name)
.cloned()
.ok_or_else(|| format_err!("missing argument {name}"))
}
pub fn get_cmd_arg(cmd_type: CmdType, args: &clap::ArgMatches<'_>) -> Vec<String> {
pub fn get_cmd_arg(cmd_type: CmdType, args: &clap::ArgMatches) -> Vec<String> {
let name = match cmd_type {
CmdType::Target => TARGET_OPTIONS,
// CmdType::Supervisor => SUPERVISOR_OPTIONS,
CmdType::Generator => GENERATOR_OPTIONS,
};
args.values_of_lossy(name).unwrap_or_default()
args.get_many::<String>(name)
.unwrap_or_default()
.cloned()
.collect()
}
pub fn get_cmd_env(
cmd_type: CmdType,
args: &clap::ArgMatches<'_>,
) -> Result<HashMap<String, String>> {
pub fn get_cmd_env(cmd_type: CmdType, args: &clap::ArgMatches) -> Result<HashMap<String, String>> {
let env_name = match cmd_type {
CmdType::Target => TARGET_ENV,
// CmdType::Supervisor => SUPERVISOR_ENV,
@ -118,58 +119,58 @@ pub fn get_cmd_env(
get_hash_map(args, env_name)
}
pub fn add_common_config(app: App<'static, 'static>) -> App<'static, 'static> {
pub fn add_common_config(app: Command) -> Command {
app.arg(
Arg::with_name("job_id")
Arg::new("job_id")
.long("job_id")
.takes_value(true)
.required(false),
.required(false)
.value_parser(value_parser!(uuid::Uuid)),
)
.arg(
Arg::with_name("task_id")
Arg::new("task_id")
.long("task_id")
.takes_value(true)
.required(false),
.required(false)
.value_parser(value_parser!(uuid::Uuid)),
)
.arg(
Arg::with_name("instance_id")
Arg::new("instance_id")
.long("instance_id")
.takes_value(true)
.required(false),
.required(false)
.value_parser(value_parser!(uuid::Uuid)),
)
.arg(
Arg::with_name("setup_dir")
Arg::new("setup_dir")
.long("setup_dir")
.takes_value(true)
.required(false),
.required(false)
.value_parser(value_parser!(PathBuf)),
)
.arg(
Arg::with_name(CREATE_JOB_DIR)
Arg::new(CREATE_JOB_DIR)
.long(CREATE_JOB_DIR)
.action(ArgAction::SetTrue)
.required(false)
.help("create a local job directory to sync the files"),
)
}
fn get_uuid(name: &str, args: &ArgMatches<'_>) -> Result<Uuid> {
value_t!(args, name, String).map(|x| {
Uuid::parse_str(&x).map_err(|x| format_err!("invalid {}. uuid expected. {})", name, x))
})?
fn get_uuid(name: &str, args: &ArgMatches) -> Result<Uuid> {
args.get_one::<Uuid>(name)
.copied()
.ok_or_else(|| format_err!("missing argument {name}"))
}
pub fn get_synced_dirs(
name: &str,
job_id: Uuid,
task_id: Uuid,
args: &ArgMatches<'_>,
args: &ArgMatches,
) -> Result<Vec<SyncedDir>> {
let create_job_dir = args.is_present(CREATE_JOB_DIR);
let create_job_dir = args.get_flag(CREATE_JOB_DIR);
let current_dir = std::env::current_dir()?;
args.values_of_os(name)
args.get_many::<PathBuf>(name)
.ok_or_else(|| anyhow!("argument '{}' not specified", name))?
.enumerate()
.map(|(index, remote_path)| {
let path = PathBuf::from(remote_path);
.map(|(index, path)| {
if create_job_dir {
let remote_path = path.absolutize()?;
let remote_url = Url::from_file_path(remote_path).expect("invalid file path");
@ -182,7 +183,7 @@ pub fn get_synced_dirs(
} else {
Ok(SyncedDir {
remote_path: None,
local_path: path,
local_path: path.clone(),
})
}
})
@ -193,10 +194,14 @@ pub fn get_synced_dir(
name: &str,
job_id: Uuid,
task_id: Uuid,
args: &ArgMatches<'_>,
args: &ArgMatches,
) -> Result<SyncedDir> {
let remote_path = value_t!(args, name, PathBuf)?.absolutize()?.into_owned();
if args.is_present(CREATE_JOB_DIR) {
let remote_path = args
.get_one::<PathBuf>(name)
.ok_or_else(|| format_err!("missing argument {name}"))?
.absolutize()?
.into_owned();
if args.get_flag(CREATE_JOB_DIR) {
let remote_url =
Url::from_file_path(remote_path).map_err(|_| anyhow!("invalid file path"))?;
let remote_blob_url = BlobContainerUrl::new(remote_url)?;
@ -218,11 +223,11 @@ pub fn get_synced_dir(
// enables making the one-shot crash report generation, which isn't really a task,
// consistent across multiple runs.
pub async fn build_local_context(
args: &ArgMatches<'_>,
args: &ArgMatches,
generate_task_id: bool,
event_sender: Option<Sender<UiEvent>>,
) -> Result<LocalContext> {
let job_id = get_uuid("job_id", args).unwrap_or_else(|_| Uuid::nil());
let job_id = get_uuid("job_id", args).unwrap_or_default();
let task_id = get_uuid("task_id", args).unwrap_or_else(|_| {
if generate_task_id {
Uuid::new_v4()
@ -230,12 +235,12 @@ pub async fn build_local_context(
Uuid::nil()
}
});
let instance_id = get_uuid("instance_id", args).unwrap_or_else(|_| Uuid::nil());
let instance_id = get_uuid("instance_id", args).unwrap_or_default();
let setup_dir = if args.is_present(SETUP_DIR) {
value_t!(args, SETUP_DIR, PathBuf)?
} else if args.is_present(TARGET_EXE) {
value_t!(args, TARGET_EXE, PathBuf)?
let setup_dir = if let Some(setup_dir) = args.get_one::<PathBuf>(SETUP_DIR) {
setup_dir.clone()
} else if let Some(target_exe) = args.get_one::<String>(TARGET_EXE) {
PathBuf::from(target_exe)
.parent()
.map(|x| x.to_path_buf())
.unwrap_or_default()

View File

@ -1,6 +1,8 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
use std::path::PathBuf;
use crate::{
local::common::{
build_local_context, get_cmd_arg, get_cmd_env, get_cmd_exe, get_synced_dir,
@ -13,14 +15,14 @@ use crate::{
},
};
use anyhow::Result;
use clap::{App, Arg, SubCommand};
use clap::{Arg, ArgAction, Command};
use flume::Sender;
use storage_queue::QueueClient;
use super::common::{SyncCountDirMonitor, UiEvent};
pub fn build_coverage_config(
args: &clap::ArgMatches<'_>,
args: &clap::ArgMatches,
local_job: bool,
input_queue: Option<QueueClient>,
common: CommonConfig,
@ -29,7 +31,7 @@ pub fn build_coverage_config(
let target_exe = get_cmd_exe(CmdType::Target, args)?.into();
let target_env = get_cmd_env(CmdType::Target, args)?;
let mut target_options = get_cmd_arg(CmdType::Target, args);
let target_timeout = value_t!(args, TARGET_TIMEOUT, u64).ok();
let target_timeout = args.get_one::<u64>(TARGET_TIMEOUT).copied();
let readonly_inputs = if local_job {
vec![
@ -67,7 +69,7 @@ pub fn build_coverage_config(
Ok(config)
}
pub async fn run(args: &clap::ArgMatches<'_>, event_sender: Option<Sender<UiEvent>>) -> Result<()> {
pub async fn run(args: &clap::ArgMatches, event_sender: Option<Sender<UiEvent>>) -> Result<()> {
let context = build_local_context(args, true, event_sender.clone()).await?;
let config = build_coverage_config(
args,
@ -81,54 +83,47 @@ pub async fn run(args: &clap::ArgMatches<'_>, event_sender: Option<Sender<UiEven
task.run().await
}
pub fn build_shared_args(local_job: bool) -> Vec<Arg<'static, 'static>> {
pub fn build_shared_args(local_job: bool) -> Vec<Arg> {
let mut args = vec![
Arg::with_name(TARGET_EXE)
.long(TARGET_EXE)
.takes_value(true)
.required(true),
Arg::with_name(TARGET_ENV)
.long(TARGET_ENV)
.takes_value(true)
.multiple(true),
Arg::with_name(TARGET_OPTIONS)
Arg::new(TARGET_EXE).long(TARGET_EXE).required(true),
Arg::new(TARGET_ENV).long(TARGET_ENV).num_args(0..),
Arg::new(TARGET_OPTIONS)
.long(TARGET_OPTIONS)
.default_value("{input}")
.takes_value(true)
.value_delimiter(" ")
.value_delimiter(' ')
.help("Use a quoted string with space separation to denote multiple arguments"),
Arg::with_name(TARGET_TIMEOUT)
.takes_value(true)
Arg::new(TARGET_TIMEOUT)
.value_parser(value_parser!(u64))
.long(TARGET_TIMEOUT),
Arg::with_name(COVERAGE_DIR)
.takes_value(true)
Arg::new(COVERAGE_DIR)
.required(!local_job)
.value_parser(value_parser!(PathBuf))
.long(COVERAGE_DIR),
Arg::with_name(CHECK_FUZZER_HELP)
.takes_value(false)
Arg::new(CHECK_FUZZER_HELP)
.action(ArgAction::SetTrue)
.long(CHECK_FUZZER_HELP),
];
if local_job {
args.push(
Arg::with_name(INPUTS_DIR)
Arg::new(INPUTS_DIR)
.long(INPUTS_DIR)
.takes_value(true)
.required(true),
.required(true)
.value_parser(value_parser!(PathBuf)),
)
} else {
args.push(
Arg::with_name(READONLY_INPUTS)
.takes_value(true)
Arg::new(READONLY_INPUTS)
.required(true)
.long(READONLY_INPUTS)
.multiple(true),
.value_parser(value_parser!(PathBuf))
.num_args(1..),
)
}
args
}
pub fn args(name: &'static str) -> App<'static, 'static> {
SubCommand::with_name(name)
pub fn args(name: &'static str) -> Command {
Command::new(name)
.about("execute a local-only coverage task")
.args(&build_shared_args(false))
}

View File

@ -1,6 +1,8 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
use std::path::PathBuf;
use crate::{
local::common::{
build_local_context, get_cmd_arg, get_cmd_exe, get_hash_map, get_synced_dir, CmdType,
@ -14,12 +16,12 @@ use crate::{
},
};
use anyhow::Result;
use clap::{App, Arg, SubCommand};
use clap::{Arg, Command};
use flume::Sender;
use storage_queue::QueueClient;
pub fn build_analysis_config(
args: &clap::ArgMatches<'_>,
args: &clap::ArgMatches,
input_queue: Option<QueueClient>,
common: CommonConfig,
event_sender: Option<Sender<UiEvent>>,
@ -27,8 +29,17 @@ pub fn build_analysis_config(
let target_exe = get_cmd_exe(CmdType::Target, args)?.into();
let target_options = get_cmd_arg(CmdType::Target, args);
let analyzer_exe = value_t!(args, ANALYZER_EXE, String)?;
let analyzer_options = args.values_of_lossy(ANALYZER_OPTIONS).unwrap_or_default();
let analyzer_exe = args
.get_one::<String>(ANALYZER_EXE)
.cloned()
.ok_or_else(|| format_err!("expected {ANALYZER_EXE}"))?;
let analyzer_options = args
.get_many::<String>(ANALYZER_OPTIONS)
.unwrap_or_default()
.map(|x| x.to_string())
.collect();
let analyzer_env = get_hash_map(args, ANALYZER_ENV)?;
let analysis = get_synced_dir(ANALYSIS_DIR, common.job_id, common.task_id, args)?
.monitor_count(&event_sender)?;
@ -69,61 +80,54 @@ pub fn build_analysis_config(
Ok(config)
}
pub async fn run(args: &clap::ArgMatches<'_>, event_sender: Option<Sender<UiEvent>>) -> Result<()> {
pub async fn run(args: &clap::ArgMatches, event_sender: Option<Sender<UiEvent>>) -> Result<()> {
let context = build_local_context(args, true, event_sender.clone()).await?;
let config = build_analysis_config(args, None, context.common_config.clone(), event_sender)?;
run_analysis(config).await
}
pub fn build_shared_args(required_task: bool) -> Vec<Arg<'static, 'static>> {
pub fn build_shared_args(required_task: bool) -> Vec<Arg> {
vec![
Arg::with_name(TARGET_EXE)
.long(TARGET_EXE)
.takes_value(true)
.required(true),
Arg::with_name(TARGET_ENV)
Arg::new(TARGET_EXE).long(TARGET_EXE).required(true),
Arg::new(TARGET_ENV)
.long(TARGET_ENV)
.requires(TARGET_EXE)
.takes_value(true)
.multiple(true),
Arg::with_name(TARGET_OPTIONS)
.num_args(0..),
Arg::new(TARGET_OPTIONS)
.long(TARGET_OPTIONS)
.takes_value(true)
.default_value("{input}")
.value_delimiter(" ")
.value_delimiter(' ')
.help("Use a quoted string with space separation to denote multiple arguments"),
Arg::with_name(CRASHES_DIR)
Arg::new(CRASHES_DIR)
.long(CRASHES_DIR)
.takes_value(true),
Arg::with_name(ANALYZER_OPTIONS)
.value_parser(value_parser!(PathBuf)),
Arg::new(ANALYZER_OPTIONS)
.long(ANALYZER_OPTIONS)
.requires(ANALYZER_EXE)
.takes_value(true)
.value_delimiter(" ")
.value_delimiter(' ')
.help("Use a quoted string with space separation to denote multiple arguments"),
Arg::with_name(ANALYZER_ENV)
Arg::new(ANALYZER_ENV)
.long(ANALYZER_ENV)
.requires(ANALYZER_EXE)
.takes_value(true)
.multiple(true),
Arg::with_name(TOOLS_DIR).long(TOOLS_DIR).takes_value(true),
Arg::with_name(ANALYZER_EXE)
.num_args(0..),
Arg::new(TOOLS_DIR)
.long(TOOLS_DIR)
.value_parser(value_parser!(PathBuf)),
Arg::new(ANALYZER_EXE)
.long(ANALYZER_EXE)
.takes_value(true)
.requires(ANALYSIS_DIR)
.requires(CRASHES_DIR)
.required(required_task),
Arg::with_name(ANALYSIS_DIR)
Arg::new(ANALYSIS_DIR)
.long(ANALYSIS_DIR)
.takes_value(true)
.requires(ANALYZER_EXE)
.requires(CRASHES_DIR)
.required(required_task),
]
}
pub fn args(name: &'static str) -> App<'static, 'static> {
SubCommand::with_name(name)
pub fn args(name: &'static str) -> Command {
Command::new(name)
.about("execute a local-only generic analysis")
.args(&build_shared_args(true))
}

View File

@ -1,6 +1,8 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
use std::path::PathBuf;
use crate::{
local::common::{
build_local_context, get_cmd_arg, get_cmd_env, get_cmd_exe, get_synced_dir, CmdType,
@ -14,12 +16,12 @@ use crate::{
},
};
use anyhow::Result;
use clap::{App, Arg, SubCommand};
use clap::{Arg, ArgAction, Command};
use flume::Sender;
use storage_queue::QueueClient;
pub fn build_report_config(
args: &clap::ArgMatches<'_>,
args: &clap::ArgMatches,
input_queue: Option<QueueClient>,
common: CommonConfig,
event_sender: Option<Sender<UiEvent>>,
@ -50,12 +52,16 @@ pub fn build_report_config(
)?)
.monitor_count(&event_sender)?;
let target_timeout = value_t!(args, TARGET_TIMEOUT, u64).ok();
let target_timeout = args.get_one::<u64>(TARGET_TIMEOUT).copied();
let check_retry_count = value_t!(args, CHECK_RETRY_COUNT, u64)?;
let check_queue = !args.is_present(DISABLE_CHECK_QUEUE);
let check_asan_log = args.is_present(CHECK_ASAN_LOG);
let check_debugger = !args.is_present(DISABLE_CHECK_DEBUGGER);
let check_retry_count = args
.get_one::<u64>(CHECK_RETRY_COUNT)
.copied()
.expect("has a default");
let check_queue = !args.get_flag(DISABLE_CHECK_QUEUE);
let check_asan_log = args.get_flag(CHECK_ASAN_LOG);
let check_debugger = !args.get_flag(DISABLE_CHECK_DEBUGGER);
let config = Config {
target_exe,
@ -78,66 +84,59 @@ pub fn build_report_config(
Ok(config)
}
pub async fn run(args: &clap::ArgMatches<'_>, event_sender: Option<Sender<UiEvent>>) -> Result<()> {
pub async fn run(args: &clap::ArgMatches, event_sender: Option<Sender<UiEvent>>) -> Result<()> {
let context = build_local_context(args, true, event_sender.clone()).await?;
let config = build_report_config(args, None, context.common_config.clone(), event_sender)?;
ReportTask::new(config).managed_run().await
}
pub fn build_shared_args() -> Vec<Arg<'static, 'static>> {
pub fn build_shared_args() -> Vec<Arg> {
vec![
Arg::with_name(TARGET_EXE)
.long(TARGET_EXE)
.takes_value(true)
.required(true),
Arg::with_name(TARGET_ENV)
.long(TARGET_ENV)
.takes_value(true)
.multiple(true),
Arg::with_name(TARGET_OPTIONS)
Arg::new(TARGET_EXE).long(TARGET_EXE).required(true),
Arg::new(TARGET_ENV).long(TARGET_ENV).num_args(0..),
Arg::new(TARGET_OPTIONS)
.default_value("{input}")
.long(TARGET_OPTIONS)
.takes_value(true)
.value_delimiter(" ")
.value_delimiter(' ')
.help("Use a quoted string with space separation to denote multiple arguments"),
Arg::with_name(CRASHES_DIR)
Arg::new(CRASHES_DIR)
.long(CRASHES_DIR)
.takes_value(true)
.required(true),
Arg::with_name(REPORTS_DIR)
.required(true)
.value_parser(value_parser!(PathBuf)),
Arg::new(REPORTS_DIR)
.long(REPORTS_DIR)
.takes_value(true)
.required(false),
Arg::with_name(NO_REPRO_DIR)
.required(false)
.value_parser(value_parser!(PathBuf)),
Arg::new(NO_REPRO_DIR)
.long(NO_REPRO_DIR)
.takes_value(true)
.required(false),
Arg::with_name(UNIQUE_REPORTS_DIR)
.required(false)
.value_parser(value_parser!(PathBuf)),
Arg::new(UNIQUE_REPORTS_DIR)
.long(UNIQUE_REPORTS_DIR)
.takes_value(true)
.value_parser(value_parser!(PathBuf))
.required(true),
Arg::with_name(TARGET_TIMEOUT)
.takes_value(true)
Arg::new(TARGET_TIMEOUT)
.long(TARGET_TIMEOUT)
.value_parser(value_parser!(u64))
.default_value("30"),
Arg::with_name(CHECK_RETRY_COUNT)
.takes_value(true)
Arg::new(CHECK_RETRY_COUNT)
.long(CHECK_RETRY_COUNT)
.value_parser(value_parser!(u64))
.default_value("0"),
Arg::with_name(DISABLE_CHECK_QUEUE)
.takes_value(false)
Arg::new(DISABLE_CHECK_QUEUE)
.action(ArgAction::SetTrue)
.long(DISABLE_CHECK_QUEUE),
Arg::with_name(CHECK_ASAN_LOG)
.takes_value(false)
Arg::new(CHECK_ASAN_LOG)
.action(ArgAction::SetTrue)
.long(CHECK_ASAN_LOG),
Arg::with_name(DISABLE_CHECK_DEBUGGER)
.takes_value(false)
Arg::new(DISABLE_CHECK_DEBUGGER)
.action(ArgAction::SetTrue)
.long(DISABLE_CHECK_DEBUGGER),
]
}
pub fn args(name: &'static str) -> App<'static, 'static> {
SubCommand::with_name(name)
pub fn args(name: &'static str) -> Command {
Command::new(name)
.about("execute a local-only generic crash report")
.args(&build_shared_args())
}

View File

@ -1,6 +1,8 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
use std::path::PathBuf;
use crate::{
local::common::{
build_local_context, get_cmd_arg, get_cmd_env, get_cmd_exe, get_synced_dir,
@ -15,11 +17,11 @@ use crate::{
},
};
use anyhow::Result;
use clap::{App, Arg, SubCommand};
use clap::{Arg, ArgAction, Command};
use flume::Sender;
pub fn build_fuzz_config(
args: &clap::ArgMatches<'_>,
args: &clap::ArgMatches,
common: CommonConfig,
event_sender: Option<Sender<UiEvent>>,
) -> Result<Config> {
@ -37,11 +39,20 @@ pub fn build_fuzz_config(
.map(|sd| sd.monitor_count(&event_sender))
.collect::<Result<Vec<_>>>()?;
let rename_output = args.is_present(RENAME_OUTPUT);
let check_asan_log = args.is_present(CHECK_ASAN_LOG);
let check_debugger = !args.is_present(DISABLE_CHECK_DEBUGGER);
let check_retry_count = value_t!(args, CHECK_RETRY_COUNT, u64)?;
let target_timeout = Some(value_t!(args, TARGET_TIMEOUT, u64)?);
let rename_output = args.get_flag(RENAME_OUTPUT);
let check_asan_log = args.get_flag(CHECK_ASAN_LOG);
let check_debugger = !args.get_flag(DISABLE_CHECK_DEBUGGER);
let check_retry_count = args
.get_one::<u64>(CHECK_RETRY_COUNT)
.copied()
.expect("has a default");
let target_timeout = Some(
args.get_one::<u64>(TARGET_TIMEOUT)
.copied()
.expect("has a default"),
);
let tools = get_synced_dir(TOOLS_DIR, common.job_id, common.task_id, args)
.ok()
@ -71,75 +82,65 @@ pub fn build_fuzz_config(
Ok(config)
}
pub async fn run(args: &clap::ArgMatches<'_>, event_sender: Option<Sender<UiEvent>>) -> Result<()> {
pub async fn run(args: &clap::ArgMatches, event_sender: Option<Sender<UiEvent>>) -> Result<()> {
let context = build_local_context(args, true, event_sender.clone()).await?;
let config = build_fuzz_config(args, context.common_config.clone(), event_sender)?;
GeneratorTask::new(config).run().await
}
pub fn build_shared_args() -> Vec<Arg<'static, 'static>> {
pub fn build_shared_args() -> Vec<Arg> {
vec![
Arg::with_name(TARGET_EXE)
.long(TARGET_EXE)
.takes_value(true)
.required(true),
Arg::with_name(TARGET_ENV)
.long(TARGET_ENV)
.takes_value(true)
.multiple(true),
Arg::with_name(TARGET_OPTIONS)
Arg::new(TARGET_EXE).long(TARGET_EXE).required(true),
Arg::new(TARGET_ENV).long(TARGET_ENV).num_args(0..),
Arg::new(TARGET_OPTIONS)
.default_value("{input}")
.long(TARGET_OPTIONS)
.takes_value(true)
.value_delimiter(" ")
.value_delimiter(' ')
.help("Use a quoted string with space separation to denote multiple arguments"),
Arg::with_name(GENERATOR_EXE)
Arg::new(GENERATOR_EXE)
.long(GENERATOR_EXE)
.default_value("radamsa")
.takes_value(true)
.required(true),
Arg::with_name(GENERATOR_ENV)
.long(GENERATOR_ENV)
.takes_value(true)
.multiple(true),
Arg::with_name(GENERATOR_OPTIONS)
Arg::new(GENERATOR_ENV).long(GENERATOR_ENV).num_args(0..),
Arg::new(GENERATOR_OPTIONS)
.long(GENERATOR_OPTIONS)
.takes_value(true)
.value_delimiter(" ")
.value_delimiter(' ')
.default_value("-H sha256 -o {generated_inputs}/input-%h.%s -n 100 -r {input_corpus}")
.help("Use a quoted string with space separation to denote multiple arguments"),
Arg::with_name(CRASHES_DIR)
.takes_value(true)
Arg::new(CRASHES_DIR)
.required(true)
.long(CRASHES_DIR),
Arg::with_name(READONLY_INPUTS)
.takes_value(true)
.long(CRASHES_DIR)
.value_parser(value_parser!(PathBuf)),
Arg::new(READONLY_INPUTS)
.required(true)
.multiple(true)
.num_args(1..)
.value_parser(value_parser!(PathBuf))
.long(READONLY_INPUTS),
Arg::with_name(TOOLS_DIR).takes_value(true).long(TOOLS_DIR),
Arg::with_name(CHECK_RETRY_COUNT)
.takes_value(true)
Arg::new(TOOLS_DIR)
.long(TOOLS_DIR)
.value_parser(value_parser!(PathBuf)),
Arg::new(CHECK_RETRY_COUNT)
.long(CHECK_RETRY_COUNT)
.value_parser(value_parser!(u64))
.default_value("0"),
Arg::with_name(CHECK_ASAN_LOG)
.takes_value(false)
Arg::new(CHECK_ASAN_LOG)
.action(ArgAction::SetTrue)
.long(CHECK_ASAN_LOG),
Arg::with_name(RENAME_OUTPUT)
.takes_value(false)
Arg::new(RENAME_OUTPUT)
.action(ArgAction::SetTrue)
.long(RENAME_OUTPUT),
Arg::with_name(TARGET_TIMEOUT)
.takes_value(true)
Arg::new(TARGET_TIMEOUT)
.long(TARGET_TIMEOUT)
.value_parser(value_parser!(u64))
.default_value("30"),
Arg::with_name(DISABLE_CHECK_DEBUGGER)
.takes_value(false)
Arg::new(DISABLE_CHECK_DEBUGGER)
.action(ArgAction::SetTrue)
.long(DISABLE_CHECK_DEBUGGER),
]
}
pub fn args(name: &'static str) -> App<'static, 'static> {
SubCommand::with_name(name)
pub fn args(name: &'static str) -> Command {
Command::new(name)
.about("execute a local-only generator fuzzing task")
.args(&build_shared_args())
}

View File

@ -26,14 +26,14 @@ use crate::{
},
};
use anyhow::Result;
use clap::{App, SubCommand};
use clap::Command;
use flume::Sender;
use onefuzz::utils::try_wait_all_join_handles;
use std::collections::HashSet;
use tokio::task::spawn;
use uuid::Uuid;
pub async fn run(args: &clap::ArgMatches<'_>, event_sender: Option<Sender<UiEvent>>) -> Result<()> {
pub async fn run(args: &clap::ArgMatches, event_sender: Option<Sender<UiEvent>>) -> Result<()> {
let context = build_local_context(args, true, event_sender.clone()).await?;
let fuzz_config = build_fuzz_config(args, context.common_config.clone(), event_sender.clone())?;
let crash_dir = fuzz_config
@ -51,7 +51,7 @@ pub async fn run(args: &clap::ArgMatches<'_>, event_sender: Option<Sender<UiEven
task_handles.push(fuzz_task);
if args.is_present(UNIQUE_REPORTS_DIR) {
if args.contains_id(UNIQUE_REPORTS_DIR) {
let crash_report_input_monitor =
DirectoryMonitorQueue::start_monitoring(crash_dir.clone()).await?;
@ -73,7 +73,7 @@ pub async fn run(args: &clap::ArgMatches<'_>, event_sender: Option<Sender<UiEven
}
#[cfg(any(target_os = "linux", target_os = "windows"))]
if args.is_present(COVERAGE_DIR) {
if args.contains_id(COVERAGE_DIR) {
let coverage_input_monitor =
DirectoryMonitorQueue::start_monitoring(crash_dir.clone()).await?;
let coverage_config = coverage::build_coverage_config(
@ -94,7 +94,7 @@ pub async fn run(args: &clap::ArgMatches<'_>, event_sender: Option<Sender<UiEven
task_handles.push(coverage_input_monitor.handle);
}
if args.is_present(ANALYZER_EXE) {
if args.contains_id(ANALYZER_EXE) {
let analysis_input_monitor = DirectoryMonitorQueue::start_monitoring(crash_dir).await?;
let analysis_config = build_analysis_config(
args,
@ -111,7 +111,7 @@ pub async fn run(args: &clap::ArgMatches<'_>, event_sender: Option<Sender<UiEven
task_handles.push(analysis_input_monitor.handle);
}
if args.is_present(REGRESSION_REPORTS_DIR) {
if args.contains_id(REGRESSION_REPORTS_DIR) {
let regression_config = build_regression_config(
args,
CommonConfig {
@ -130,12 +130,12 @@ pub async fn run(args: &clap::ArgMatches<'_>, event_sender: Option<Sender<UiEven
Ok(())
}
pub fn args(name: &'static str) -> App<'static, 'static> {
let mut app = SubCommand::with_name(name).about("run a local libfuzzer & crash reporting task");
pub fn args(name: &'static str) -> Command {
let mut app = Command::new(name).about("run a local libfuzzer & crash reporting task");
let mut used = HashSet::new();
for args in [
for args in &[
build_fuzz_args(),
build_crash_args(),
build_analysis_args(false),
@ -144,11 +144,9 @@ pub fn args(name: &'static str) -> App<'static, 'static> {
build_regression_args(false),
] {
for arg in args {
if used.contains(arg.b.name) {
continue;
if used.insert(arg.get_id()) {
app = app.arg(arg);
}
used.insert(arg.b.name.to_string());
app = app.arg(arg);
}
}

View File

@ -1,6 +1,8 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
use std::path::PathBuf;
use crate::{
local::common::{
build_local_context, get_cmd_arg, get_cmd_env, get_cmd_exe, get_synced_dir, CmdType,
@ -14,12 +16,12 @@ use crate::{
},
};
use anyhow::Result;
use clap::{App, Arg, SubCommand};
use clap::{Arg, ArgAction, Command};
use flume::Sender;
use storage_queue::QueueClient;
pub fn build_report_config(
args: &clap::ArgMatches<'_>,
args: &clap::ArgMatches,
input_queue: Option<QueueClient>,
common: CommonConfig,
event_sender: Option<Sender<UiEvent>>,
@ -43,13 +45,16 @@ pub fn build_report_config(
.ok()
.monitor_count(&event_sender)?;
let target_timeout = value_t!(args, TARGET_TIMEOUT, u64).ok();
let target_timeout = args.get_one::<u64>(TARGET_TIMEOUT).copied();
let check_retry_count = value_t!(args, CHECK_RETRY_COUNT, u64)?;
let check_retry_count = args
.get_one::<u64>(CHECK_RETRY_COUNT)
.copied()
.expect("has a default");
let check_queue = !args.is_present(DISABLE_CHECK_QUEUE);
let check_queue = !args.get_flag(DISABLE_CHECK_QUEUE);
let check_fuzzer_help = args.is_present(CHECK_FUZZER_HELP);
let check_fuzzer_help = args.get_flag(CHECK_FUZZER_HELP);
let crashes = if input_queue.is_none() { crashes } else { None };
@ -73,61 +78,54 @@ pub fn build_report_config(
Ok(config)
}
pub async fn run(args: &clap::ArgMatches<'_>, event_sender: Option<Sender<UiEvent>>) -> Result<()> {
pub async fn run(args: &clap::ArgMatches, event_sender: Option<Sender<UiEvent>>) -> Result<()> {
let context = build_local_context(args, true, event_sender.clone()).await?;
let config = build_report_config(args, None, context.common_config.clone(), event_sender)?;
ReportTask::new(config).managed_run().await
}
pub fn build_shared_args() -> Vec<Arg<'static, 'static>> {
pub fn build_shared_args() -> Vec<Arg> {
vec![
Arg::with_name(TARGET_EXE)
.long(TARGET_EXE)
.takes_value(true)
.required(true),
Arg::with_name(TARGET_ENV)
.long(TARGET_ENV)
.takes_value(true)
.multiple(true),
Arg::with_name(TARGET_OPTIONS)
Arg::new(TARGET_EXE).long(TARGET_EXE).required(true),
Arg::new(TARGET_ENV).long(TARGET_ENV).num_args(0..),
Arg::new(TARGET_OPTIONS)
.long(TARGET_OPTIONS)
.takes_value(true)
.value_delimiter(" ")
.value_delimiter(' ')
.help("Use a quoted string with space separation to denote multiple arguments"),
Arg::with_name(CRASHES_DIR)
Arg::new(CRASHES_DIR)
.long(CRASHES_DIR)
.takes_value(true)
.required(true),
Arg::with_name(REPORTS_DIR)
.required(true)
.value_parser(value_parser!(PathBuf)),
Arg::new(REPORTS_DIR)
.long(REPORTS_DIR)
.takes_value(true)
.required(false),
Arg::with_name(NO_REPRO_DIR)
.required(false)
.value_parser(value_parser!(PathBuf)),
Arg::new(NO_REPRO_DIR)
.long(NO_REPRO_DIR)
.takes_value(true)
.required(false),
Arg::with_name(UNIQUE_REPORTS_DIR)
.required(false)
.value_parser(value_parser!(PathBuf)),
Arg::new(UNIQUE_REPORTS_DIR)
.long(UNIQUE_REPORTS_DIR)
.takes_value(true)
.required(true),
Arg::with_name(TARGET_TIMEOUT)
.takes_value(true)
.required(true)
.value_parser(value_parser!(PathBuf)),
Arg::new(TARGET_TIMEOUT)
.value_parser(value_parser!(u64))
.long(TARGET_TIMEOUT),
Arg::with_name(CHECK_RETRY_COUNT)
.takes_value(true)
Arg::new(CHECK_RETRY_COUNT)
.long(CHECK_RETRY_COUNT)
.value_parser(value_parser!(u64))
.default_value("0"),
Arg::with_name(DISABLE_CHECK_QUEUE)
.takes_value(false)
Arg::new(DISABLE_CHECK_QUEUE)
.action(ArgAction::SetTrue)
.long(DISABLE_CHECK_QUEUE),
Arg::with_name(CHECK_FUZZER_HELP)
.takes_value(false)
Arg::new(CHECK_FUZZER_HELP)
.action(ArgAction::SetTrue)
.long(CHECK_FUZZER_HELP),
]
}
pub fn args(name: &'static str) -> App<'static, 'static> {
SubCommand::with_name(name)
pub fn args(name: &'static str) -> Command {
Command::new(name)
.about("execute a local-only libfuzzer crash report task")
.args(&build_shared_args())
}

View File

@ -1,6 +1,8 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
use std::path::PathBuf;
use crate::{
local::common::{
build_local_context, get_cmd_arg, get_cmd_env, get_cmd_exe, get_synced_dir, CmdType,
@ -13,13 +15,13 @@ use crate::{
},
};
use anyhow::Result;
use clap::{App, Arg, SubCommand};
use clap::{Arg, ArgAction, Command};
use flume::Sender;
const EXPECT_CRASH_ON_FAILURE: &str = "expect_crash_on_failure";
pub fn build_fuzz_config(
args: &clap::ArgMatches<'_>,
args: &clap::ArgMatches,
common: CommonConfig,
event_sender: Option<Sender<UiEvent>>,
) -> Result<Config> {
@ -32,10 +34,14 @@ pub fn build_fuzz_config(
let target_env = get_cmd_env(CmdType::Target, args)?;
let target_options = get_cmd_arg(CmdType::Target, args);
let target_workers = value_t!(args, "target_workers", usize).unwrap_or_default();
let target_workers = args
.get_one::<usize>("target_workers")
.copied()
.unwrap_or_default();
let readonly_inputs = None;
let check_fuzzer_help = args.is_present(CHECK_FUZZER_HELP);
let expect_crash_on_failure = args.is_present(EXPECT_CRASH_ON_FAILURE);
let check_fuzzer_help = args.get_flag(CHECK_FUZZER_HELP);
let expect_crash_on_failure = args.get_flag(EXPECT_CRASH_ON_FAILURE);
let ensemble_sync_delay = None;
@ -57,49 +63,42 @@ pub fn build_fuzz_config(
Ok(config)
}
pub async fn run(args: &clap::ArgMatches<'_>, event_sender: Option<Sender<UiEvent>>) -> Result<()> {
pub async fn run(args: &clap::ArgMatches, event_sender: Option<Sender<UiEvent>>) -> Result<()> {
let context = build_local_context(args, true, event_sender.clone()).await?;
let config = build_fuzz_config(args, context.common_config.clone(), event_sender)?;
LibFuzzerFuzzTask::new(config)?.run().await
}
pub fn build_shared_args() -> Vec<Arg<'static, 'static>> {
pub fn build_shared_args() -> Vec<Arg> {
vec![
Arg::with_name(TARGET_EXE)
.long(TARGET_EXE)
.takes_value(true)
.required(true),
Arg::with_name(TARGET_ENV)
.long(TARGET_ENV)
.takes_value(true)
.multiple(true),
Arg::with_name(TARGET_OPTIONS)
Arg::new(TARGET_EXE).long(TARGET_EXE).required(true),
Arg::new(TARGET_ENV).long(TARGET_ENV).num_args(0..),
Arg::new(TARGET_OPTIONS)
.long(TARGET_OPTIONS)
.takes_value(true)
.value_delimiter(" ")
.value_delimiter(' ')
.help("Use a quoted string with space separation to denote multiple arguments"),
Arg::with_name(INPUTS_DIR)
Arg::new(INPUTS_DIR)
.long(INPUTS_DIR)
.takes_value(true)
.required(true),
Arg::with_name(CRASHES_DIR)
.required(true)
.value_parser(value_parser!(PathBuf)),
Arg::new(CRASHES_DIR)
.long(CRASHES_DIR)
.takes_value(true)
.required(true),
Arg::with_name(TARGET_WORKERS)
.required(true)
.value_parser(value_parser!(PathBuf)),
Arg::new(TARGET_WORKERS)
.long(TARGET_WORKERS)
.takes_value(true),
Arg::with_name(CHECK_FUZZER_HELP)
.takes_value(false)
.value_parser(value_parser!(u64)),
Arg::new(CHECK_FUZZER_HELP)
.action(ArgAction::SetTrue)
.long(CHECK_FUZZER_HELP),
Arg::with_name(EXPECT_CRASH_ON_FAILURE)
.takes_value(false)
Arg::new(EXPECT_CRASH_ON_FAILURE)
.action(ArgAction::SetTrue)
.long(EXPECT_CRASH_ON_FAILURE),
]
}
pub fn args(name: &'static str) -> App<'static, 'static> {
SubCommand::with_name(name)
pub fn args(name: &'static str) -> Command {
Command::new(name)
.about("execute a local-only libfuzzer fuzzing task")
.args(&build_shared_args())
}

View File

@ -1,6 +1,8 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
use std::path::PathBuf;
use crate::{
local::common::{
build_local_context, get_cmd_arg, get_cmd_env, get_cmd_exe, get_synced_dir,
@ -14,12 +16,12 @@ use crate::{
},
};
use anyhow::Result;
use clap::{App, Arg, SubCommand};
use clap::{Arg, ArgAction, Command};
use flume::Sender;
use storage_queue::QueueClient;
pub fn build_merge_config(
args: &clap::ArgMatches<'_>,
args: &clap::ArgMatches,
input_queue: Option<QueueClient>,
common: CommonConfig,
event_sender: Option<Sender<UiEvent>>,
@ -27,7 +29,7 @@ pub fn build_merge_config(
let target_exe = get_cmd_exe(CmdType::Target, args)?.into();
let target_env = get_cmd_env(CmdType::Target, args)?;
let target_options = get_cmd_arg(CmdType::Target, args);
let check_fuzzer_help = args.is_present(CHECK_FUZZER_HELP);
let check_fuzzer_help = args.get_flag(CHECK_FUZZER_HELP);
let inputs = get_synced_dirs(ANALYSIS_INPUTS, common.job_id, common.task_id, args)?
.into_iter()
.map(|sd| sd.monitor_count(&event_sender))
@ -35,7 +37,10 @@ pub fn build_merge_config(
let unique_inputs =
get_synced_dir(ANALYSIS_UNIQUE_INPUTS, common.job_id, common.task_id, args)?
.monitor_count(&event_sender)?;
let preserve_existing_outputs = value_t!(args, PRESERVE_EXISTING_OUTPUTS, bool)?;
let preserve_existing_outputs = args
.get_one::<bool>(PRESERVE_EXISTING_OUTPUTS)
.copied()
.unwrap_or_default();
let config = Config {
target_exe,
@ -52,39 +57,32 @@ pub fn build_merge_config(
Ok(config)
}
pub async fn run(args: &clap::ArgMatches<'_>, event_sender: Option<Sender<UiEvent>>) -> Result<()> {
pub async fn run(args: &clap::ArgMatches, event_sender: Option<Sender<UiEvent>>) -> Result<()> {
let context = build_local_context(args, true, event_sender.clone()).await?;
let config = build_merge_config(args, None, context.common_config.clone(), event_sender)?;
spawn(std::sync::Arc::new(config)).await
}
pub fn build_shared_args() -> Vec<Arg<'static, 'static>> {
pub fn build_shared_args() -> Vec<Arg> {
vec![
Arg::with_name(TARGET_EXE)
.long(TARGET_EXE)
.takes_value(true)
.required(true),
Arg::with_name(TARGET_ENV)
.long(TARGET_ENV)
.takes_value(true)
.multiple(true),
Arg::with_name(TARGET_OPTIONS)
Arg::new(TARGET_EXE).long(TARGET_EXE).required(true),
Arg::new(TARGET_ENV).long(TARGET_ENV).num_args(0..),
Arg::new(TARGET_OPTIONS)
.long(TARGET_OPTIONS)
.takes_value(true)
.value_delimiter(" ")
.value_delimiter(' ')
.help("Use a quoted string with space separation to denote multiple arguments"),
Arg::with_name(CHECK_FUZZER_HELP)
.takes_value(false)
Arg::new(CHECK_FUZZER_HELP)
.action(ArgAction::SetTrue)
.long(CHECK_FUZZER_HELP),
Arg::with_name(INPUTS_DIR)
Arg::new(INPUTS_DIR)
.long(INPUTS_DIR)
.takes_value(true)
.multiple(true),
.value_parser(value_parser!(PathBuf))
.num_args(0..),
]
}
pub fn args(name: &'static str) -> App<'static, 'static> {
SubCommand::with_name(name)
pub fn args(name: &'static str) -> Command {
Command::new(name)
.about("execute a local-only libfuzzer crash report task")
.args(&build_shared_args())
}

View File

@ -1,6 +1,8 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
use std::path::PathBuf;
use crate::{
local::common::{
build_local_context, get_cmd_arg, get_cmd_env, get_cmd_exe, get_synced_dir, CmdType,
@ -14,26 +16,29 @@ use crate::{
},
};
use anyhow::Result;
use clap::{App, Arg, SubCommand};
use clap::{Arg, ArgAction, Command};
use flume::Sender;
const REPORT_NAMES: &str = "report_names";
pub fn build_regression_config(
args: &clap::ArgMatches<'_>,
args: &clap::ArgMatches,
common: CommonConfig,
event_sender: Option<Sender<UiEvent>>,
) -> Result<Config> {
let target_exe = get_cmd_exe(CmdType::Target, args)?.into();
let target_env = get_cmd_env(CmdType::Target, args)?;
let target_options = get_cmd_arg(CmdType::Target, args);
let target_timeout = value_t!(args, TARGET_TIMEOUT, u64).ok();
let target_timeout = args.get_one::<u64>(TARGET_TIMEOUT).copied();
let crashes = get_synced_dir(CRASHES_DIR, common.job_id, common.task_id, args)?
.monitor_count(&event_sender)?;
let regression_reports =
get_synced_dir(REGRESSION_REPORTS_DIR, common.job_id, common.task_id, args)?
.monitor_count(&event_sender)?;
let check_retry_count = value_t!(args, CHECK_RETRY_COUNT, u64)?;
let check_retry_count = args
.get_one::<u64>(CHECK_RETRY_COUNT)
.copied()
.expect("has a default value");
let reports = get_synced_dir(REPORTS_DIR, common.job_id, common.task_id, args)
.ok()
@ -45,13 +50,11 @@ pub fn build_regression_config(
.ok()
.monitor_count(&event_sender)?;
let report_list = if args.is_present(REPORT_NAMES) {
Some(values_t!(args, REPORT_NAMES, String)?)
} else {
None
};
let report_list: Option<Vec<String>> = args
.get_many::<String>(REPORT_NAMES)
.map(|x| x.cloned().collect());
let check_fuzzer_help = args.is_present(CHECK_FUZZER_HELP);
let check_fuzzer_help = args.get_flag(CHECK_FUZZER_HELP);
let config = Config {
target_exe,
@ -73,75 +76,63 @@ pub fn build_regression_config(
Ok(config)
}
pub async fn run(args: &clap::ArgMatches<'_>, event_sender: Option<Sender<UiEvent>>) -> Result<()> {
pub async fn run(args: &clap::ArgMatches, event_sender: Option<Sender<UiEvent>>) -> Result<()> {
let context = build_local_context(args, true, event_sender.clone()).await?;
let config = build_regression_config(args, context.common_config.clone(), event_sender)?;
LibFuzzerRegressionTask::new(config).run().await
}
pub fn build_shared_args(local_job: bool) -> Vec<Arg<'static, 'static>> {
pub fn build_shared_args(local_job: bool) -> Vec<Arg> {
let mut args = vec![
Arg::with_name(TARGET_EXE)
.long(TARGET_EXE)
.takes_value(true)
.required(true),
Arg::with_name(TARGET_ENV)
.long(TARGET_ENV)
.takes_value(true)
.multiple(true),
Arg::with_name(TARGET_OPTIONS)
Arg::new(TARGET_EXE).long(TARGET_EXE).required(true),
Arg::new(TARGET_ENV).long(TARGET_ENV).num_args(0..),
Arg::new(TARGET_OPTIONS)
.long(TARGET_OPTIONS)
.takes_value(true)
.value_delimiter(" ")
.value_delimiter(' ')
.help("Use a quoted string with space separation to denote multiple arguments"),
Arg::with_name(COVERAGE_DIR)
.takes_value(true)
Arg::new(COVERAGE_DIR)
.required(!local_job)
.long(COVERAGE_DIR),
Arg::with_name(CHECK_FUZZER_HELP)
.takes_value(false)
.long(COVERAGE_DIR)
.value_parser(value_parser!(PathBuf)),
Arg::new(CHECK_FUZZER_HELP)
.action(ArgAction::SetTrue)
.long(CHECK_FUZZER_HELP),
Arg::with_name(TARGET_TIMEOUT)
.takes_value(true)
.long(TARGET_TIMEOUT),
Arg::with_name(CRASHES_DIR)
Arg::new(TARGET_TIMEOUT)
.long(TARGET_TIMEOUT)
.value_parser(value_parser!(u64)),
Arg::new(CRASHES_DIR)
.long(CRASHES_DIR)
.takes_value(true)
.required(true),
Arg::with_name(REGRESSION_REPORTS_DIR)
.required(true)
.value_parser(value_parser!(PathBuf)),
Arg::new(REGRESSION_REPORTS_DIR)
.long(REGRESSION_REPORTS_DIR)
.takes_value(true)
.required(local_job),
Arg::with_name(REPORTS_DIR)
.required(local_job)
.value_parser(value_parser!(PathBuf)),
Arg::new(REPORTS_DIR)
.long(REPORTS_DIR)
.takes_value(true)
.required(false),
Arg::with_name(NO_REPRO_DIR)
.required(false)
.value_parser(value_parser!(PathBuf)),
Arg::new(NO_REPRO_DIR)
.long(NO_REPRO_DIR)
.takes_value(true)
.required(false),
Arg::with_name(UNIQUE_REPORTS_DIR)
.required(false)
.value_parser(value_parser!(PathBuf)),
Arg::new(UNIQUE_REPORTS_DIR)
.long(UNIQUE_REPORTS_DIR)
.takes_value(true)
.value_parser(value_parser!(PathBuf))
.required(true),
Arg::with_name(CHECK_RETRY_COUNT)
.takes_value(true)
Arg::new(CHECK_RETRY_COUNT)
.long(CHECK_RETRY_COUNT)
.value_parser(value_parser!(u64))
.default_value("0"),
];
if local_job {
args.push(
Arg::with_name(REPORT_NAMES)
.long(REPORT_NAMES)
.takes_value(true)
.multiple(true),
)
args.push(Arg::new(REPORT_NAMES).long(REPORT_NAMES).num_args(0..))
}
args
}
pub fn args(name: &'static str) -> App<'static, 'static> {
SubCommand::with_name(name)
pub fn args(name: &'static str) -> Command {
Command::new(name)
.about("execute a local-only libfuzzer regression task")
.args(&build_shared_args(false))
.args(&build_shared_args(true))
}

View File

@ -9,19 +9,26 @@ use crate::{
tasks::report::libfuzzer_report::{test_input, TestInputArgs},
};
use anyhow::Result;
use clap::{App, Arg, SubCommand};
use clap::{Arg, Command};
use flume::Sender;
use std::path::PathBuf;
pub async fn run(args: &clap::ArgMatches<'_>, event_sender: Option<Sender<UiEvent>>) -> Result<()> {
pub async fn run(args: &clap::ArgMatches, event_sender: Option<Sender<UiEvent>>) -> Result<()> {
let context = build_local_context(args, true, event_sender).await?;
let target_exe = value_t!(args, TARGET_EXE, PathBuf)?;
let target_exe = args
.get_one::<PathBuf>(TARGET_EXE)
.expect("marked as required");
let target_env = get_cmd_env(CmdType::Target, args)?;
let target_options = get_cmd_arg(CmdType::Target, args);
let input = value_t!(args, "input", PathBuf)?;
let target_timeout = value_t!(args, TARGET_TIMEOUT, u64).ok();
let check_retry_count = value_t!(args, CHECK_RETRY_COUNT, u64)?;
let input = args
.get_one::<PathBuf>("input")
.expect("marked as required");
let target_timeout = args.get_one::<u64>(TARGET_TIMEOUT).copied();
let check_retry_count = args
.get_one::<u64>(CHECK_RETRY_COUNT)
.copied()
.expect("has a default value");
let config = TestInputArgs {
target_exe: target_exe.as_path(),
@ -43,32 +50,30 @@ pub async fn run(args: &clap::ArgMatches<'_>, event_sender: Option<Sender<UiEven
Ok(())
}
pub fn build_shared_args() -> Vec<Arg<'static, 'static>> {
pub fn build_shared_args() -> Vec<Arg> {
vec![
Arg::with_name(TARGET_EXE).takes_value(true).required(true),
Arg::with_name("input").takes_value(true).required(true),
Arg::with_name(TARGET_ENV)
.long(TARGET_ENV)
.takes_value(true)
.multiple(true),
Arg::with_name(TARGET_OPTIONS)
Arg::new(TARGET_EXE).required(true),
Arg::new("input")
.required(true)
.value_parser(value_parser!(PathBuf)),
Arg::new(TARGET_ENV).long(TARGET_ENV).num_args(0..),
Arg::new(TARGET_OPTIONS)
.default_value("{input}")
.long(TARGET_OPTIONS)
.takes_value(true)
.value_delimiter(" ")
.value_delimiter(' ')
.help("Use a quoted string with space separation to denote multiple arguments"),
Arg::with_name(TARGET_TIMEOUT)
.takes_value(true)
.long(TARGET_TIMEOUT),
Arg::with_name(CHECK_RETRY_COUNT)
.takes_value(true)
Arg::new(TARGET_TIMEOUT)
.long(TARGET_TIMEOUT)
.value_parser(value_parser!(u64)),
Arg::new(CHECK_RETRY_COUNT)
.long(CHECK_RETRY_COUNT)
.value_parser(value_parser!(u64))
.default_value("0"),
]
}
pub fn args(name: &'static str) -> App<'static, 'static> {
SubCommand::with_name(name)
pub fn args(name: &'static str) -> Command {
Command::new(name)
.about("test a libfuzzer application with a specific input")
.args(&build_shared_args())
}

View File

@ -10,14 +10,14 @@ use crate::{
tasks::{config::CommonConfig, fuzz::generator::GeneratorTask, report::generic::ReportTask},
};
use anyhow::{Context, Result};
use clap::{App, SubCommand};
use clap::Command;
use flume::Sender;
use onefuzz::utils::try_wait_all_join_handles;
use std::collections::HashSet;
use tokio::task::spawn;
use uuid::Uuid;
pub async fn run(args: &clap::ArgMatches<'_>, event_sender: Option<Sender<UiEvent>>) -> Result<()> {
pub async fn run(args: &clap::ArgMatches, event_sender: Option<Sender<UiEvent>>) -> Result<()> {
let context = build_local_context(args, true, event_sender.clone()).await?;
let fuzz_config = build_fuzz_config(args, context.common_config.clone(), event_sender.clone())?;
let crash_dir = fuzz_config
@ -62,17 +62,15 @@ pub async fn run(args: &clap::ArgMatches<'_>, event_sender: Option<Sender<UiEven
Ok(())
}
pub fn args(name: &'static str) -> App<'static, 'static> {
let mut app = SubCommand::with_name(name).about("run a local generator & crash reporting job");
pub fn args(name: &'static str) -> Command {
let mut app = Command::new(name).about("run a local generator & crash reporting job");
let mut used = HashSet::new();
for args in [build_fuzz_args(), build_crash_args()] {
for args in &[build_fuzz_args(), build_crash_args()] {
for arg in args {
if used.contains(arg.b.name) {
continue;
if used.insert(arg.get_id()) {
app = app.arg(arg);
}
used.insert(arg.b.name.to_string());
app = app.arg(arg);
}
}

View File

@ -10,21 +10,28 @@ use crate::{
tasks::report::generic::{test_input, TestInputArgs},
};
use anyhow::Result;
use clap::{App, Arg, SubCommand};
use clap::{Arg, ArgAction, Command};
use flume::Sender;
use std::path::PathBuf;
pub async fn run(args: &clap::ArgMatches<'_>, event_sender: Option<Sender<UiEvent>>) -> Result<()> {
pub async fn run(args: &clap::ArgMatches, event_sender: Option<Sender<UiEvent>>) -> Result<()> {
let context = build_local_context(args, false, event_sender).await?;
let target_exe = value_t!(args, TARGET_EXE, PathBuf)?;
let target_exe = args
.get_one::<PathBuf>(TARGET_EXE)
.expect("is marked required");
let target_env = get_cmd_env(CmdType::Target, args)?;
let target_options = get_cmd_arg(CmdType::Target, args);
let input = value_t!(args, "input", PathBuf)?;
let target_timeout = value_t!(args, TARGET_TIMEOUT, u64).ok();
let check_retry_count = value_t!(args, CHECK_RETRY_COUNT, u64)?;
let check_asan_log = args.is_present(CHECK_ASAN_LOG);
let check_debugger = !args.is_present(DISABLE_CHECK_DEBUGGER);
let input = args
.get_one::<PathBuf>("input")
.expect("is marked required");
let target_timeout = args.get_one::<u64>(TARGET_TIMEOUT).copied();
let check_retry_count = args
.get_one::<u64>(CHECK_RETRY_COUNT)
.copied()
.expect("has default value");
let check_asan_log = args.get_flag(CHECK_ASAN_LOG);
let check_debugger = !args.get_flag(DISABLE_CHECK_DEBUGGER);
let config = TestInputArgs {
target_exe: target_exe.as_path(),
@ -48,38 +55,36 @@ pub async fn run(args: &clap::ArgMatches<'_>, event_sender: Option<Sender<UiEven
Ok(())
}
pub fn build_shared_args() -> Vec<Arg<'static, 'static>> {
pub fn build_shared_args() -> Vec<Arg> {
vec![
Arg::with_name(TARGET_EXE).takes_value(true).required(true),
Arg::with_name("input").takes_value(true).required(true),
Arg::with_name(TARGET_ENV)
.long(TARGET_ENV)
.takes_value(true)
.multiple(true),
Arg::with_name(TARGET_OPTIONS)
Arg::new(TARGET_EXE).required(true),
Arg::new("input")
.required(true)
.value_parser(value_parser!(PathBuf)),
Arg::new(TARGET_ENV).long(TARGET_ENV).num_args(0..),
Arg::new(TARGET_OPTIONS)
.default_value("{input}")
.long(TARGET_OPTIONS)
.takes_value(true)
.value_delimiter(" ")
.value_delimiter(' ')
.help("Use a quoted string with space separation to denote multiple arguments"),
Arg::with_name(TARGET_TIMEOUT)
.takes_value(true)
.long(TARGET_TIMEOUT),
Arg::with_name(CHECK_RETRY_COUNT)
.takes_value(true)
Arg::new(TARGET_TIMEOUT)
.long(TARGET_TIMEOUT)
.value_parser(value_parser!(u64)),
Arg::new(CHECK_RETRY_COUNT)
.long(CHECK_RETRY_COUNT)
.value_parser(value_parser!(u64))
.default_value("0"),
Arg::with_name(CHECK_ASAN_LOG)
.takes_value(false)
Arg::new(CHECK_ASAN_LOG)
.action(ArgAction::SetTrue)
.long(CHECK_ASAN_LOG),
Arg::with_name(DISABLE_CHECK_DEBUGGER)
.takes_value(false)
Arg::new(DISABLE_CHECK_DEBUGGER)
.action(ArgAction::SetTrue)
.long("disable_check_debugger"),
]
}
pub fn args(name: &'static str) -> App<'static, 'static> {
SubCommand::with_name(name)
pub fn args(name: &'static str) -> Command {
Command::new(name)
.about("test an application with a specific input")
.args(&build_shared_args())
}

View File

@ -10,7 +10,7 @@ extern crate onefuzz_telemetry;
extern crate onefuzz;
use anyhow::Result;
use clap::{App, ArgMatches, SubCommand};
use clap::{ArgMatches, Command};
use std::io::{stdout, Write};
mod local;
@ -29,11 +29,11 @@ fn main() -> Result<()> {
env!("GIT_VERSION")
);
let app = App::new("onefuzz-task")
.version(built_version.as_str())
let app = Command::new("onefuzz-task")
.version(built_version)
.subcommand(managed::cmd::args(MANAGED_CMD))
.subcommand(local::cmd::args(LOCAL_CMD))
.subcommand(SubCommand::with_name(LICENSE_CMD).about("display third-party licenses"));
.subcommand(Command::new(LICENSE_CMD).about("display third-party licenses"));
let matches = app.get_matches();
@ -43,7 +43,7 @@ fn main() -> Result<()> {
result
}
async fn run(args: ArgMatches<'static>) -> Result<()> {
async fn run(args: ArgMatches) -> Result<()> {
// It'd be best to initialize these environment vars in the same abstraction that
// pulls in user-provided task vars that set the environment, e.g. `target_env`.
// For now, just ensure that sanitizer environment vars will be inherited by child
@ -51,12 +51,10 @@ async fn run(args: ArgMatches<'static>) -> Result<()> {
set_sanitizer_env_vars()?;
match args.subcommand() {
(LICENSE_CMD, Some(_)) => licenses(),
(LOCAL_CMD, Some(sub)) => local::cmd::run(sub.to_owned()).await,
(MANAGED_CMD, Some(sub)) => managed::cmd::run(sub).await,
_ => {
anyhow::bail!("missing subcommand\nUSAGE: {}", args.usage());
}
Some((LICENSE_CMD, _)) => licenses(),
Some((LOCAL_CMD, sub)) => local::cmd::run(sub.to_owned()).await,
Some((MANAGED_CMD, sub)) => managed::cmd::run(sub).await,
_ => anyhow::bail!("No command provided. Run with 'help' to see available commands."),
}
}

View File

@ -3,7 +3,7 @@
use std::path::PathBuf;
use anyhow::Result;
use clap::{App, Arg, SubCommand};
use clap::{Arg, Command};
use std::time::Duration;
use crate::tasks::{
@ -13,10 +13,17 @@ use crate::tasks::{
const OOM_CHECK_INTERVAL: Duration = Duration::from_secs(5);
pub async fn run(args: &clap::ArgMatches<'_>) -> Result<()> {
pub async fn run(args: &clap::ArgMatches) -> Result<()> {
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")).init();
let config_path = value_t!(args, "config", PathBuf)?;
let setup_dir = value_t!(args, "setup_dir", PathBuf)?;
let config_path = args
.get_one::<PathBuf>("config")
.expect("marked as required");
let setup_dir = args
.get_one::<PathBuf>("setup_dir")
.expect("marked as required");
let config = Config::from_file(config_path, setup_dir)?;
init_telemetry(config.common()).await;
@ -118,9 +125,17 @@ async fn init_telemetry(config: &CommonConfig) {
.await;
}
pub fn args(name: &str) -> App<'static, 'static> {
SubCommand::with_name(name)
pub fn args(name: &'static str) -> Command {
Command::new(name)
.about("managed fuzzing")
.arg(Arg::with_name("config").required(true))
.arg(Arg::with_name("setup_dir").required(true))
.arg(
Arg::new("config")
.required(true)
.value_parser(value_parser!(PathBuf)),
)
.arg(
Arg::new("setup_dir")
.required(true)
.value_parser(value_parser!(PathBuf)),
)
}

View File

@ -17,7 +17,11 @@ use onefuzz_telemetry::{
};
use reqwest::Url;
use serde::{self, Deserialize};
use std::{path::PathBuf, sync::Arc, time::Duration};
use std::{
path::{Path, PathBuf},
sync::Arc,
time::Duration,
};
use uuid::Uuid;
const DEFAULT_MIN_AVAILABLE_MEMORY_MB: u64 = 100;
@ -135,13 +139,13 @@ pub enum Config {
}
impl Config {
pub fn from_file(path: PathBuf, setup_dir: PathBuf) -> Result<Self> {
pub fn from_file(path: &Path, setup_dir: &Path) -> Result<Self> {
let json = std::fs::read_to_string(path)?;
let json_config: serde_json::Value = serde_json::from_str(&json)?;
// override the setup_dir in the config file with the parameter value if specified
let mut config: Self = serde_json::from_value(json_config)?;
config.common_mut().setup_dir = setup_dir;
config.common_mut().setup_dir = setup_dir.to_owned();
Ok(config)
}

View File

@ -85,7 +85,7 @@ impl CheckNotify for tokio::sync::Notify {
}
}
pub fn parse_key_value(value: String) -> Result<(String, String)> {
pub fn parse_key_value(value: &str) -> Result<(String, String)> {
let offset = value
.find('=')
.ok_or_else(|| format_err!("invalid key=value, no = found {:?}", value))?;

View File

@ -60,8 +60,8 @@ nix = "0.25"
[target.'cfg(target_os = "linux")'.dependencies]
pete = "0.9"
rstack = "0.3"
proc-maps = "0.2"
proc-maps = { version = "0.3", default-features = false }
[dev-dependencies]
structopt = "0.3"
clap = { version = "4.1.6", features = ["derive"] }
pretty_assertions = "1.3.0"

View File

@ -2,18 +2,18 @@
// Licensed under the MIT License.
use anyhow::Result;
use clap::Parser;
use onefuzz::monitor::DirectoryMonitor;
use structopt::StructOpt;
#[derive(Debug, StructOpt)]
#[derive(Debug, Parser)]
struct Opt {
#[structopt(short, long)]
#[arg(short, long)]
path: String,
}
#[tokio::main]
async fn main() -> Result<()> {
let opt = Opt::from_args();
let opt = Opt::parse();
let mut monitor = DirectoryMonitor::new(opt.path).await?;
monitor.set_report_directories(true);

View File

@ -4,40 +4,40 @@
use std::path::PathBuf;
use anyhow::Result;
use clap::Parser;
use onefuzz::{input_tester::Tester, machine_id::MachineIdentity};
use structopt::StructOpt;
#[derive(Debug, PartialEq, Eq, StructOpt)]
#[structopt(name = "test-input")]
#[derive(Debug, PartialEq, Eq, Parser)]
#[command(name = "test-input")]
struct Opt {
#[structopt(short, long)]
#[arg(short, long)]
pub exe: PathBuf,
#[structopt(short, long, long_help = "Defaults to `{input}`")]
#[arg(short, long, long_help = "Defaults to `{input}`")]
pub options: Vec<String>,
#[structopt(short, long, long_help = "Defaults to dir of `exe`")]
#[arg(short, long, long_help = "Defaults to dir of `exe`")]
pub setup_dir: Option<PathBuf>,
#[structopt(short, long)]
#[arg(short, long)]
pub input: PathBuf,
#[structopt(long)]
#[arg(long)]
pub check_asan_log: bool,
#[structopt(long)]
#[arg(long)]
pub check_asan_stderr: bool,
#[structopt(long)]
#[arg(long)]
pub no_check_debugger: bool,
#[structopt(short, long, long_help = "Timeout (seconds)", default_value = "5")]
#[arg(short, long, long_help = "Timeout (seconds)", default_value = "5")]
pub timeout: u64,
}
#[tokio::main]
async fn main() -> Result<()> {
let opt = Opt::from_args();
let opt = Opt::parse();
// Default `setup_dir` to base dir of
let setup_dir = opt.setup_dir.clone().unwrap_or_else(|| {

View File

@ -17,5 +17,5 @@ regex = "1"
serde = { version = "1", features = ["derive"] }
quick-xml = "0.27"
anyhow = "1.0"
structopt = "0.3"
env_logger = "0.9"
env_logger = "0.10"
clap = { version = "4.1.6", features = ["derive"] }

View File

@ -2,13 +2,13 @@
// Licensed under the MIT License.
use anyhow::{format_err, Context, Result};
use clap::Parser;
use srcview::{ModOff, Report, SrcLine, SrcView};
use std::fs::{self, OpenOptions};
use std::io::{stdout, BufWriter, Write};
use std::path::{Path, PathBuf};
use structopt::StructOpt;
#[derive(StructOpt, Debug)]
#[derive(Parser, Debug)]
enum Opt {
Srcloc(SrcLocOpt),
PdbPaths(PdbPathsOpt),
@ -18,17 +18,17 @@ enum Opt {
}
/// Print the file paths in the provided PDB
#[derive(StructOpt, Debug)]
#[derive(Parser, Debug)]
struct PdbPathsOpt {
pdb_path: PathBuf,
}
/// Print modoffset file with file and source lines
#[derive(StructOpt, Debug)]
#[derive(Parser, Debug)]
struct SrcLocOpt {
pdb_path: PathBuf,
modoff_path: PathBuf,
#[structopt(long)]
#[arg(long)]
module_name: Option<String>,
}
@ -45,30 +45,30 @@ struct SrcLocOpt {
///
/// The XML report is written to either a file or stdout if the argument is
/// a single dash.
#[derive(StructOpt, Debug)]
#[derive(Parser, Debug)]
struct CoberturaOpt {
pdb_path: PathBuf,
modoff_path: PathBuf,
#[structopt(default_value = "-")]
#[arg(default_value = "-")]
output_path: String,
#[structopt(long)]
#[arg(long)]
module_name: Option<String>,
/// regular expression that will be applied against the file paths from the
/// srcview
#[structopt(long)]
#[arg(long)]
include_regex: Option<String>,
/// search and replace regular expression that is applied to all file
/// paths that will appear in the output report
#[structopt(long)]
#[arg(long)]
filter_regex: Option<String>,
}
fn main() -> Result<()> {
env_logger::init();
let opt = Opt::from_args();
let opt = Opt::parse();
match opt {
Opt::Srcloc(opts) => srcloc(opts)?,

View File

@ -16,8 +16,6 @@ unsound = "deny"
yanked = "deny"
ignore = [
"RUSTSEC-2022-0048", # xml-rs is unmaintained
"RUSTSEC-2021-0139", # ansi_term is unmaintained
"RUSTSEC-2021-0145", # waiting for clap upgrade; we are unaffected by the bug (no custom allocator)
]
[bans]

View File

@ -51,17 +51,6 @@ dependencies = [
"syn 1.0.76",
]
[[package]]
name = "atty"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [
"hermit-abi 0.1.19",
"libc",
"winapi",
]
[[package]]
name = "autocfg"
version = "1.1.0"
@ -145,13 +134,13 @@ dependencies = [
[[package]]
name = "clap"
version = "4.0.26"
version = "4.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2148adefda54e14492fb9bddcc600b4344c5d1a3123bd666dcb939c6f0e0e57e"
checksum = "ec0b0588d44d4d63a87dbd75c136c166bbfd9a86a31cb89e09906521c7d3f5e3"
dependencies = [
"atty",
"bitflags",
"clap_lex",
"is-terminal",
"once_cell",
"strsim",
"termcolor",
@ -230,17 +219,38 @@ dependencies = [
[[package]]
name = "env_logger"
version = "0.9.0"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b2cf0344971ee6c64c31be0d530793fba457d322dfec2810c453d0ef228f9c3"
checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0"
dependencies = [
"atty",
"humantime",
"is-terminal",
"log",
"regex",
"termcolor",
]
[[package]]
name = "errno"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1"
dependencies = [
"errno-dragonfly",
"libc",
"winapi",
]
[[package]]
name = "errno-dragonfly"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
dependencies = [
"cc",
"libc",
]
[[package]]
name = "flume"
version = "0.10.9"
@ -418,15 +428,6 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
[[package]]
name = "hermit-abi"
version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
dependencies = [
"libc",
]
[[package]]
name = "hermit-abi"
version = "0.2.6"
@ -554,12 +555,34 @@ dependencies = [
"cfg-if",
]
[[package]]
name = "io-lifetimes"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7d6c6f8c91b4b9ed43484ad1a938e393caf35960fce7f82a040497207bd8e9e"
dependencies = [
"libc",
"windows-sys 0.42.0",
]
[[package]]
name = "ipnet"
version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68f2d64f2edebec4ce84ad108148e67e1064789bee435edc5b60ad398714a3a9"
[[package]]
name = "is-terminal"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28dfb6c8100ccc63462345b67d1bbc3679177c75ee4bf59bf29c8b1d110b8189"
dependencies = [
"hermit-abi",
"io-lifetimes",
"rustix",
"windows-sys 0.42.0",
]
[[package]]
name = "itoa"
version = "0.4.8"
@ -589,9 +612,15 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.126"
version = "0.2.139"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836"
checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79"
[[package]]
name = "linux-raw-sys"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4"
[[package]]
name = "lock_api"
@ -700,7 +729,7 @@ version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b"
dependencies = [
"hermit-abi 0.2.6",
"hermit-abi",
"libc",
]
@ -1044,6 +1073,20 @@ dependencies = [
"thiserror",
]
[[package]]
name = "rustix"
version = "0.36.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4fdebc4b395b7fbb9ab11e462e20ed9051e7b16e42d24042c776eca0ac81b03"
dependencies = [
"bitflags",
"errno",
"io-lifetimes",
"libc",
"linux-raw-sys",
"windows-sys 0.42.0",
]
[[package]]
name = "ryu"
version = "1.0.5"

View File

@ -9,7 +9,7 @@ license = "MIT"
[dependencies]
anyhow = "1.0"
clap = { version = "4", features = ["cargo", "string"] }
env_logger = "0.9"
env_logger = "0.10"
futures = "0.3"
reqwest = { version = "0.11", features = [
"json",