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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
mod gen_stub;
mod importer;
mod parser;
mod resolver;
use argh::FromArgs;
use log::{error, info, warn};
use std::collections::{HashMap, HashSet};
use std::env;
use std::error::Error;
#[derive(FromArgs)]
/// Generate stubs for use with snmp-rust-agent
struct Cli {
/// output directory
#[argh(
option,
short = 'o',
default = "String::from(\"../snmp-rust/src/stubs/\")"
)]
out_dir: String,
/// MIB Search path elements
#[argh(
option,
short = 'p',
default = "importer::MIB_SEARCH_PATH.iter().map(|s|s.to_string()).collect()"
)]
path: Vec<String>,
/// include deprecated
#[argh(switch, short = 'd')]
deprecated: bool,
/// include obsolete
#[argh(switch)]
obsolete: bool,
/// names of MIBs to process
#[argh(positional)]
mib_names: Vec<String>,
}
impl Cli {
fn check_status(&self, status: &str) -> bool {
let status = status.trim();
if self.deprecated && status == "deprecated" {
return true;
}
if self.obsolete && status == "obsolete" {
return true;
}
status == "current"
}
}
fn main() -> Result<(), Box<dyn Error>> {
// Set sane default if environment variable does not exist
if env::var("RUST_LOG").is_err() {
println!("RUST_LOG environment variable not set, defaulting to warn");
env::set_var("RUST_LOG", "warn");
}
env_logger::init();
let cli: Cli = argh::from_env();
let mut total = 0;
let mut success = 0;
let builtins: HashSet<&str> = vec![
"TimeTicks",
"OBJECT-TYPE",
"Counter32",
"Gauge32",
"NOTIFICATION-TYPE",
"Unsigned32",
"Counter64",
"mib-2",
"transmission",
"IpAddress",
"OBJECT-IDENTITY",
"internet",
"Counter",
"snmpModules",
"snmpProxys",
"snmpDomains",
"Integer32",
"MODULE-IDENTITY",
"TEXTUAL-CONVENTION",
"NOTIFICATION-GROUP",
"OBJECT-GROUP",
"MODULE-COMPLIANCE",
"DisplayString",
"EntryStatus",
"OwnerString",
]
.into_iter()
.collect();
/*let mib_path = Path::new("/var/lib/mibs/ietf/");
for entry in fs::read_dir(mib_path)? {
let entry = entry?;
let path = entry.path();
//if !path.ends_with("UDPF-MIB") {continue;}
let text = //fs::read_to_string(&path).unwrap(); */
let out_dir = cli.out_dir.to_string();
let mut stub_ok = vec![];
for argument in &cli.mib_names.clone() {
let text_opt = importer::find_mib_text(argument, &cli.path);
if text_opt.is_none() {
warn!("Not found MIB {argument}, skipping, will try rest");
continue;
}
let text = text_opt.unwrap();
let mib_name = argument;
let mut res = resolver::Resolver::new();
let mut nodes = vec![];
total += 1;
let (good_parse, site) = parser::parse_mib(&text, &mut nodes);
if good_parse {
let imp_nodes: Vec<&parser::MibNode> = nodes
.iter()
.filter(|node| matches!(**node, parser::MibNode::Imp(_)))
.collect();
if imp_nodes.len() != 1 {
warn!("Expected one import block");
continue;
}
let imp_node = imp_nodes[0];
let val = match imp_node {
parser::MibNode::Imp(istruct) => istruct.imp_list.clone(),
_ => {
panic!("Weird! expected ImpNode here");
}
};
for (names, mib_name) in val {
let miss: Vec<String> = names
.iter()
.filter(|x| !builtins.contains(*x))
.map(|x| x.to_string())
.collect();
if !miss.is_empty() {
let txt_opt = importer::find_mib_text(mib_name, &cli.path);
if let Some(iraw) = txt_opt {
let extra = importer::process_one(&iraw, miss, mib_name, &mut res);
nodes.extend(extra);
} else {
error!("Import failed for {mib_name}, file not found");
}
}
}
let mut good_parent = false;
let inodes = nodes.clone();
for pass in 0..3 {
for node in &inodes {
match node {
parser::MibNode::ModId(o) => {
let v = &o.val;
let parent = v.parent;
let added = res.try_add(o.name, parent, &v.num);
good_parent = added;
if !added && pass > 2 {
error!("Unknown module parent {parent} {mib_name}")
}
}
parser::MibNode::ObTy(o) => {
let v = &o.val;
let parent = v.parent;
let added = res.try_add(o.name, parent, &v.num);
if good_parent && !added && pass > 1 {
error!("Unknown type parent {0} {1} {2}", o.name, parent, mib_name);
}
}
parser::MibNode::ObIdy(o) => {
let v = &o.val;
let parent = v.parent;
let added = res.try_add(o.name, v.parent, &v.num);
if good_parent && !added && pass > 1 {
error!("Unknown identity parent {parent}")
}
}
parser::MibNode::ObIdf(o) => {
let v = &o.val;
let parent = v.parent;
let added = res.try_add(o.name, v.parent, &v.num);
if good_parent && !added && pass > 1 {
error!("Unknown identifier parent {parent}")
}
}
parser::MibNode::ModCp(o) => {
let v = &o.val;
let parent = v.parent;
let added = res.try_add(o.name, v.parent, &v.num);
if good_parent && !added && pass > 1 {
error!("Unknown compliance parent {parent}")
}
}
parser::MibNode::NtTy(o) => {
/*let v = &o.val;
let parent = v.parent;
let added = res.try_add(o.name, v.parent, &v.num);
if good_parent && !added && pass > 1 {
error!("Unknown compliance parent {parent}")
}*/
if pass > 1 {
for obj in &o.objects {
if !res.check_name(obj) {
warn!(
"Unresolved object name {0} in NOTIFICATION-TYPE {1}",
obj, o.name
);
}
}
}
}
_ => (),
}
}
}
let mut object_types: HashMap<&str, parser::ObjectType> = HashMap::new();
let mut tcs: HashMap<&str, parser::TextConvention> = HashMap::new();
let mut entries: HashMap<&str, parser::Entry<'_>> = HashMap::new();
let mut object_ids: Vec<parser::ObjectIdentity<'_>> = vec![];
let mut mod_comps: Vec<parser::ModuleCompliance<'_>> = vec![];
let gnodes = nodes.clone();
for node in gnodes {
match node {
parser::MibNode::ObTy(x) => {
if cli.check_status(x.status) {
object_types.insert(x.name, x);
}
}
parser::MibNode::Tc(x) => {
if cli.check_status(x.status) {
tcs.insert(x.name, x);
}
}
parser::MibNode::Ent(x) => {
entries.insert(x.name, x);
}
parser::MibNode::ObIdy(x) => {
if cli.check_status(x.status) {
object_ids.push(x)
}
}
parser::MibNode::ModCp(x) => mod_comps.push(x),
_ => {}
}
}
// Now mark the table columns, so we don't generate scalar code for them
for entry in entries.values() {
for ent in entry.syntax.clone() {
let col_obj_res = object_types.get_mut(ent.0);
if let Some(c) = col_obj_res {
c.col = true
}
}
}
let out_res = gen_stub::open_output(mib_name, &out_dir);
if let Ok(out) = out_res {
let compile_res = gen_stub::gen_stub(
&object_types,
res,
&tcs,
&entries,
&object_ids,
&mod_comps,
out,
);
if compile_res.is_ok() {
stub_ok.push(argument.clone());
success += 1;
}
} else {
warn!("Output file open failed");
}
} else {
error!("{mib_name} {site}");
}
}
info!("{success} read out of {total}");
if gen_stub::loader(stub_ok).is_ok() {
info!("Wrote stub loader");
}
Ok(())
}