1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
|
//! Arti consensus method plugin for C Tor
//!
//! Implements `authority-plugin.md` pursuant to `doc/dev/notes/dirauth-sketch.md`.
//!
//! This is the actual implementation.
use anyhow::Context as _;
use tor_dirauth::consensus;
use tor_error::ErrorReport as _;
mod utils;
use utils::FilenameOrStdio;
/// Options and arguments to plugin invocation
#[derive(Debug, clap::Parser)]
#[command(after_help =
//
"For details of semantics, exit status, input/output formats, etc., see the spec:
arti-authority-plugin dump-spec
Latest version:
https://gitlab.torproject.org/tpo/core/arti/-/blob/main/crates/arti-dirauth/authority-plugin.md
"
)]
struct CliArgs {
/// Operation verb and its arguments
#[command(subcommand)]
op: CliOperation,
}
/// Operation verb and its arguments
#[derive(Debug, Clone, clap::Subcommand)]
enum CliOperation {
/// `list-methods`, "mode 1"
ListMethods {
/// Output file
#[arg(short = 'o')]
output: FilenameOrStdio,
},
/// Print the specification to stdout, in markdown format
//
// This is to save us from having to document everything again here.
// Instead, this can be regarded as part of the program's help output.
// Our help output has a link to the rendered version in gitlab
// (`after_help` attribute on `CliArgs`)
DumpSpec {},
}
/// Top-level error - program exits with this, or `Ok(())`
#[derive(Debug, thiserror::Error)]
enum CliError {
/// Invalid operation or usage
#[error("invalid operation or usage")]
InvalidInputs(#[source] anyhow::Error),
/// Unsupported consensus method
#[error("fall back to C Tor")]
#[allow(dead_code)] // TODO DIRAUTH
UnsupportedConsensusMethod(#[from] consensus::UnsupportedConsensusMethod),
/// Operational error
#[error("failed")]
OperationalError(#[source] anyhow::Error),
}
//==================== implementations ====================
/// Actual implementation of the plugin's invocations
///
/// Split off for ease of testing.
fn plugin_impl(args: CliArgs) -> Result<(), CliError> {
match args.op {
CliOperation::ListMethods { output } => output.write(|w| {
for m in consensus::SupportedConsensusMethod::iter_all() {
writeln!(w, "{m}")?;
}
Ok(())
}),
CliOperation::DumpSpec {} => {
print!("{}", include_str!("../authority-plugin.md"));
Ok(())
}
}
}
/// Entrypoint for the Arti-in-C-Tor consensus method plugin
pub fn plugin_main() {
match (|| {
let args = <CliArgs as clap::Parser>::try_parse()
.context("invalid arguments")
.map_err(CliError::InvalidInputs)?;
plugin_impl(args)
})() {
Ok(()) => {}
Err(e) => {
eprintln!("arti-authority-plugin: error: {}", e.report());
std::process::exit(i32::from(e.exit_status()));
}
}
}
impl CliError {
/// Exit status corresponding to this error, as per `authority-plugin.md`
///
/// Returns `u8` because that's what Unix processes can exit,
/// `std::process:exit`'s `i32` argument notwithstanding.
fn exit_status(&self) -> u8 {
use CliError as E;
match self {
E::InvalidInputs { .. } => 8,
E::UnsupportedConsensusMethod { .. } => 10,
E::OperationalError { .. } => 32,
}
}
}
|