Compare commits

..

No commits in common. "master" and "v0.1.2" have entirely different histories.

11 changed files with 122 additions and 389 deletions

View File

@ -21,17 +21,15 @@ steps:
- name: build amd64
image: rust:1.73-alpine
commands:
- apk add --no-cache musl-dev gcc-aarch64-none-elf bash git
- git fetch --all --tags
- pwd && ls -al && sh ./version.sh
- apk add --no-cache musl-dev gcc-aarch64-none-elf
- mkdir /artifacts/mouse-amd64
- cp LICENSE.txt /artifacts/mouse-amd64/
- mkdir /artifacts/mouse-arm64
- cp LICENSE.txt /artifacts/mouse-arm64/
- rustup target add x86_64-unknown-linux-musl
- rustup target add aarch64-unknown-linux-musl
- cargo build --target x86_64-unknown-linux-musl --bins -r
- cargo build --target aarch64-unknown-linux-musl --bins -r
- cargo build --target x86_64-unknown-linux-musl --bins -r --config 'package.version="${DRONE_TAG##v}"'
- cargo build --target aarch64-unknown-linux-musl --bins -r --config 'package.version="${DRONE_TAG##v}"'
- cp target/x86_64-unknown-linux-musl/release/mouse /artifacts/mouse-amd64/mouse
- cp target/aarch64-unknown-linux-musl/release/mouse /artifacts/mouse-arm64/mouse-arm64
- cd /artifacts && tar -cvzf mouse-amd64.tar.gz mouse-amd64/*

1
.gitignore vendored
View File

@ -2,4 +2,3 @@
.vscode/
*.un~
*.swp
mouse_out.yml

172
Cargo.lock generated
View File

@ -50,18 +50,6 @@ dependencies = [
"windows-sys",
]
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "clap"
version = "4.4.7"
@ -108,45 +96,6 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
[[package]]
name = "core-foundation-sys"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa"
[[package]]
name = "crossbeam-deque"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef"
dependencies = [
"cfg-if",
"crossbeam-epoch",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-epoch"
version = "0.9.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7"
dependencies = [
"autocfg",
"cfg-if",
"crossbeam-utils",
"memoffset",
"scopeguard",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294"
dependencies = [
"cfg-if",
]
[[package]]
name = "csv"
version = "1.3.0"
@ -168,12 +117,6 @@ dependencies = [
"memchr",
]
[[package]]
name = "either"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
[[package]]
name = "equivalent"
version = "1.0.1"
@ -208,33 +151,12 @@ version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
[[package]]
name = "libc"
version = "0.2.149"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b"
[[package]]
name = "linked-hash-map"
version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
[[package]]
name = "memchr"
version = "2.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
[[package]]
name = "memoffset"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c"
dependencies = [
"autocfg",
]
[[package]]
name = "mouse"
version = "0.0.0"
@ -244,25 +166,8 @@ dependencies = [
"serde",
"serde_json",
"serde_yaml",
"sysinfo",
"yaml-rust",
]
[[package]]
name = "ntapi"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4"
dependencies = [
"winapi",
]
[[package]]
name = "once_cell"
version = "1.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
[[package]]
name = "proc-macro2"
version = "1.0.69"
@ -281,38 +186,12 @@ dependencies = [
"proc-macro2",
]
[[package]]
name = "rayon"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1"
dependencies = [
"either",
"rayon-core",
]
[[package]]
name = "rayon-core"
version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed"
dependencies = [
"crossbeam-deque",
"crossbeam-utils",
]
[[package]]
name = "ryu"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
[[package]]
name = "scopeguard"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "serde"
version = "1.0.190"
@ -335,9 +214,9 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.108"
version = "1.0.107"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b"
checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65"
dependencies = [
"itoa",
"ryu",
@ -374,22 +253,6 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "sysinfo"
version = "0.29.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a18d114d420ada3a891e6bc8e96a2023402203296a47cdd65083377dad18ba5"
dependencies = [
"cfg-if",
"core-foundation-sys",
"libc",
"ntapi",
"once_cell",
"rayon",
"serde",
"winapi",
]
[[package]]
name = "unicode-ident"
version = "1.0.12"
@ -408,28 +271,6 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-sys"
version = "0.48.0"
@ -495,12 +336,3 @@ name = "windows_x86_64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
[[package]]
name = "yaml-rust"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85"
dependencies = [
"linked-hash-map",
]

View File

@ -9,10 +9,8 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
sysinfo = { version = "0.29.10", features = ["serde"] }
yaml-rust = "0.4.5"
clap = { version = "4.4.7", features = ["derive"] }
csv = "1.3.0"
serde = { version = "1.0.190", features = ["std", "derive"] }
serde_json = "1.0.108"
serde_yaml = "0.9.27"
serde = { version = "1.0.189", features = ["std", "derive"] }
serde_json = "1.0.107"
serde_yaml = "0.9.25"

View File

@ -8,38 +8,19 @@ Hence mouse :D.
## Whut?
Mouse is a rust based facts gatherer that returns a list of facts of the host, it can be completely ran as user at this time and it's intended to stay that way.
Mouse does not alter the system, just gathers information and returns it in json or yaml (default). At this time I can only test on x86_64 and aarch64, so those are the only supported platforms for now.
Mouse does not alter the system, just gathers information and returns it in a key/value way (with extra epoch timestamp).
## Why?
Yes we have ohai, facter, osquery,...
But those won't let me learn Rust.
## How?
Download the release or build locally and execute. Use --help for information.
example: `mouse -g system -o json` will output system information in json.
At this time with the latest release (v0.1.4) there are 4 gatherers:
* env
* ipaddr
* iproute
* system
## Contribute?
Why not, but don't feel obligated.
If you want to contribute, feel free to send your patches to mouse-patch@sejo-it dot be.
## It doesn't work!
Aww, too bad, well you can always submit an issue or fix it yourself. It's open source afterall, isn't it?
## I have more questions:
matrix: #mouse-dev:matrix.sejo-it.be
matrix: @sejo:matrix.sejo-it.be
discord: sejoit (legacy: sejo#5402)
email: jochen@sejo-it.be
email: jochen@sejo-it.be

View File

@ -4,7 +4,8 @@
use crate::types::fact::Fact;
use serde::{Deserialize, Serialize};
use std::{collections::HashMap, env};
use serde_json::Value;
use std::env;
pub struct EnvironmentData {}
impl EnvironmentData {}
@ -13,13 +14,18 @@ impl EnvironmentData {}
pub struct EnvironmentValue {
key: String,
value: String,
time_set: u128,
}
impl Fact for EnvironmentData {
fn gather(&self) -> String {
let mut outmap: HashMap<String, String> = HashMap::new();
let mut outmap: Vec<Value> = vec![];
for (key, value) in env::vars() {
outmap.insert(key, value);
let entry = json!({
"key": &key,
"value": value,
});
outmap.append(&mut vec![entry]);
}
serde_json::to_string(&outmap).unwrap()
}

View File

@ -3,8 +3,10 @@
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
use crate::{types::fact::Fact, util::command::get_result_as_string};
use serde::{Deserialize, Serialize};
use serde_json::Value;
#[derive(Serialize, Deserialize)]
pub struct IPData {}
impl IPData {}
@ -12,7 +14,7 @@ impl Fact for IPData {
fn gather(&self) -> String {
let res = get_result_as_string("ip", vec!["-j", "addr"]);
let jsonres: Value = serde_json::from_str(res.as_str()).unwrap();
let out = json!({ "ipaddr": jsonres });
let out = json!({ "ip_addr": jsonres });
out.to_string()
}
}
@ -24,7 +26,7 @@ impl Fact for IPRouteData {
let res = get_result_as_string("ip", vec!["-j", "route"]);
let jsonres: Value = serde_json::from_str(res.as_str()).unwrap();
let out = json!({
"iproute": jsonres
"ip_route": jsonres
});
out.to_string()
}

View File

@ -4,4 +4,3 @@
pub mod environment;
pub mod ip;
pub mod system;

View File

@ -2,133 +2,106 @@
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
use std::{fs, io::ErrorKind, str, usize};
use std::process::Command;
use sysinfo::{System, SystemExt};
use crate::types::fact::Fact;
use crate::types::fact::{Fact, FactData};
use serde::{Deserialize, Serialize};
pub struct SystemData {}
impl SystemData {}
#[derive(Serialize, Deserialize)]
struct UnameData {
kernel_name: String,
node_name: String,
kernel_release: String,
kernel_version: String,
machine: String,
processor: String,
hardware_platform: String,
operating_system: String,
}
#[derive(Serialize, Deserialize)]
struct IpAddrInfo {
family: String,
local: String,
prefixlen: i32,
broadcast: Option<String>,
scope: String,
dynamic: Option<bool>,
noprefixroute: Option<bool>,
label: Option<String>,
valid_life_time: i64,
preferred_life_time: i64,
}
#[derive(Serialize, Deserialize)]
struct IpAddr {
ifindex: i32,
ifname: String,
flags: Vec<String>,
mtu: i32,
qdisc: String,
operstate: String,
group: String,
txqlen: i32,
link_type: String,
address: Option<String>,
broadcast: Option<String>,
addr_info: Vec<IpAddrInfo>,
}
impl Fact for SystemData {
fn gather(&self) -> String {
if System::IS_SUPPORTED {
let mut sys = System::new();
// we only need certain values.
sys.refresh_cpu();
sys.refresh_disks();
sys.refresh_disks_list();
sys.refresh_memory();
sys.refresh_system();
sys.refresh_components();
sys.refresh_components_list();
sys.refresh_users_list();
serde_json::to_string(&sys).unwrap()
} else {
"{\"error\": \"Not Supported\"}".to_string()
fn gather(&self) -> Vec<FactData> {
let mut vfd: Vec<FactData> = vec![];
let time_set = self.get_epoch_ms();
let output = Command::new("ip")
.arg("-j")
.arg("addr")
.output()
.expect("Failed to execute ip");
let stdout = String::from_utf8(output.stdout).unwrap();
let ips: Vec<IpAddr> = serde_json::from_str(stdout.as_str()).unwrap();
for ip in ips {
if ip.ifname != "lo" {
for addr_info in ip.addr_info {
let fd = FactData {
name: format!("ip_addr_{}_{}_addr", ip.ifname, addr_info.family),
value: addr_info.local,
time_set: time_set,
};
vfd.push(fd);
vfd.push(FactData {
name: format!("ip_addr_{}_{}_prefixlen", ip.ifname, addr_info.family),
value: addr_info.prefixlen.to_string(),
time_set: time_set,
});
if addr_info.label.is_some() {
vfd.push(FactData {
name: format!("ip_addr_{}_{}_label", ip.ifname, addr_info.family),
value: addr_info.label.unwrap(),
time_set: time_set,
});
}
if addr_info.broadcast.is_some() {
vfd.push(FactData {
name: format!("ip_addr_{}_{}_broadcast", ip.ifname, addr_info.family),
value: addr_info.broadcast.unwrap(),
time_set: time_set,
});
}
if ip.address.is_some() {
vfd.push(FactData {
name: format!("ip_addr_{}_macaddr", ip.ifname),
value: ip.address.clone().unwrap(),
time_set: time_set,
})
}
}
}
}
return vfd;
}
}
pub struct DMIData {}
impl DMIData {}
impl Fact for DMIData {
fn gather(&self) -> String {
json!({
"form_factor": get_form_factor("/sys/devices/virtual/dmi/id/chassis_type"),
"bios_date": get_string("/sys/devices/virtual/dmi/id/bios_date"),
"bios_vendor": get_string("/sys/devices/virtual/dmi/id/bios_vendor"),
"bios_version": get_string("/sys/devices/virtual/dmi/id/bios_version"),
"board_asset_tag": get_string("/sys/devices/virtual/dmi/id/board_asset_tag"),
"board_name": get_string("/sys/devices/virtual/dmi/id/board_name"),
"board_serial": get_string("/sys/devices/virtual/dmi/id/board_serial"),
"board_vendor": get_string("/sys/devices/virtual/dmi/id/board_vendor"),
"board_version": get_string("/sys/devices/virtual/dmi/id/board_version"),
"chassis_asset_tag": get_string("/sys/devices/virtual/dmi/id/chassis_asset_tag"),
"chassis_serial": get_string("/sys/devices/virtual/dmi/id/chassis_vendor"),
"chassis_vendor": get_string("/sys/devices/virtual/dmi/id/chassis_vendor"),
"chassis_version": get_string("/sys/devices/virtual/dmi/id/chassis_version"),
"product_name": get_string("/sys/devices/virtual/dmi/id/product_name"),
"product_serial": get_string("/sys/devices/virtual/dmi/id/product_serial"),
"product_uuid": get_string("/sys/devices/virtual/dmi/id/product_uuid"),
"product_version": get_string("/sys/devices/virtual/dmi/id/product_version"),
"system_vendor": get_string("/sys/devices/virtual/dmi/id/sys_vendor"),
})
.to_string()
}
}
fn get_string(path: &str) -> String {
match fs::read(path) {
Ok(x) => String::from_utf8(x).unwrap().trim().to_string(),
Err(y) => match y.kind() {
ErrorKind::PermissionDenied => "This data needs sudo or root permissions".to_string(),
other_error => format!("Could not retrieve {other_error}"),
},
}
}
fn get_form_factor(path: &str) -> &str {
// Get form factor info
// as learned from Ansible, we need to have the list in a specific order,
// see https://www.dmtf.org/sites/default/files/standards/documents/DSP0134_3.2.0.pdf
let ff: Vec<&str> = vec![
"Unknown",
"Other",
"Unknown",
"Desktop",
"Low Profile Desktop",
"Pizza Box",
"Mini Tower",
"Tower",
"Portable",
"Laptop",
"Notebook",
"Hand Held",
"Docking Station",
"All In One",
"Sub Notebook",
"Space-saving",
"Lunch Box",
"Main Server Chassis",
"Expansion Chassis",
"Sub Chassis",
"Bus Expansion Chassis",
"Peripheral Chassis",
"RAID Chassis",
"Rack Mount Chassis",
"Sealed-case PC",
"Multi-system",
"CompactPCI",
"AdvancedTCA",
"Blade",
"Blade Enclosure",
"Tablet",
"Convertible",
"Detachable",
"IoT Gateway",
"Embedded PC",
"Mini PC",
"Stick PC",
];
let filecontent: &str = match fs::read(path) {
Err(_) => ff[0],
Ok(x) => {
let ind: i32 = match str::from_utf8(&x) {
Ok(x) => x.trim().parse::<i32>().unwrap(),
Err(_) => 0,
};
let out: &str = if ff.len() <= ind as usize {
ff[0]
} else {
ff[ind as usize]
};
out
}
};
filecontent
}

View File

@ -13,10 +13,8 @@ use crate::gatherers::environment::EnvironmentData;
use crate::gatherers::ip::{IPData, IPRouteData};
use crate::types::fact::Fact;
use clap::Parser;
use gatherers::system::{DMIData, SystemData};
use serde_json::Value;
use std::collections::{HashMap, HashSet};
use yaml_rust::{YamlEmitter, YamlLoader};
#[derive(Parser)]
#[clap(author, version, about, long_about = None)]
@ -33,12 +31,6 @@ fn gather_list(gatherers: HashSet<String>, output: &str) -> String {
let mut outmap: HashMap<String, Value> = HashMap::new();
for g in gatherers {
match g.as_str() {
"dmi" => {
outmap.insert(
"dmi".to_string(),
serde_json::from_str(&DMIData {}.gather()).unwrap(),
);
}
"env" => {
outmap.insert(
"environment".to_string(),
@ -48,27 +40,13 @@ fn gather_list(gatherers: HashSet<String>, output: &str) -> String {
"ipaddr" => {
outmap.insert(
"ipaddr".to_string(),
serde_json::from_str::<Value>(&IPData {}.gather())
.unwrap()
.get("ipaddr")
.unwrap()
.clone(),
serde_json::from_str(&IPData {}.gather()).unwrap(),
);
}
"iproute" => {
outmap.insert(
"iproute".to_string(),
serde_json::from_str::<Value>(&IPRouteData {}.gather())
.unwrap()
.get("iproute")
.unwrap()
.clone(),
);
}
"system" => {
outmap.insert(
"system".to_string(),
serde_json::from_str(&SystemData {}.gather()).unwrap(),
serde_json::from_str(&IPRouteData {}.gather()).unwrap(),
);
}
x => {
@ -77,7 +55,7 @@ fn gather_list(gatherers: HashSet<String>, output: &str) -> String {
};
}
match output {
"json" => serde_json::to_string_pretty(&outmap).unwrap(),
"json" => serde_json::to_string(&outmap).unwrap(),
"yaml" => serde_yaml::to_string(&outmap).unwrap(),
x => format!("Unknown output format {x}"),
}
@ -90,11 +68,9 @@ fn main() {
_ => "yaml",
};
let all: HashSet<String> = HashSet::from([
"dmi".to_string(),
"env".to_string(),
"ipaddr".to_string(),
"iproute".to_string(),
"system".to_string(),
]);
let data_output: String = match args.gatherer {
None => gather_list(all, output_format),
@ -103,21 +79,5 @@ fn main() {
gather_list(gatherers, output_format)
}
};
match output_format {
"json" => println!("{}", data_output),
"yaml" => {
// We will run the string back through yaml_rust, for better output compatibility.
// We could choose to swwitch serde_yaml out completely but at this time I don't see
// a huge value, as once serde_yaml fixes their implementation,...
let tmp = format!("---\n{}", data_output);
let yr = YamlLoader::load_from_str(tmp.as_str()).unwrap();
let mut out = String::new();
{
let mut emitter = YamlEmitter::new(&mut out);
emitter.dump(&yr[0]).unwrap();
};
println!("{}", out)
}
x => println!("This should never print,.. but it did. Maybe this explains why: {x}"),
}
println!("{}", data_output);
}

View File

@ -1,15 +0,0 @@
#!/bin/bash
set -x
# Get last created tag
LTCOMMIT=$(git rev-list --abbrev-commit --tags --max-count=1)
LTAG=$(git describe --abbrev=0 --tags ${LTCOMMIT} 2>/dev/null || true)
ADC=${DRONE_COMMIT:0:7}
if [[ "${LTCOMMIT}" == "${ADC}" ]]; then
VERSION="${DRONE_TAG##v}"
else
VERSION="${LTAG##v}-next-${ADC}"
fi
sed -i "s/0\\.0\\.0/${VERSION}/" Cargo.toml
cat Cargo.toml