A => .gitignore +2 -0
@@ 1,2 @@
+/target
+**/*.swp
A => Cargo.lock +233 -0
@@ 1,233 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "aho-corasick"
+version = "0.7.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "bitflags"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "clap"
+version = "2.33.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002"
+dependencies = [
+ "bitflags",
+ "textwrap",
+ "unicode-width",
+]
+
+[[package]]
+name = "heck"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87cbf45460356b7deeb5e3415b5563308c0a9b057c85e12b06ad551f98d0a6ac"
+dependencies = [
+ "unicode-segmentation",
+]
+
+[[package]]
+name = "lazy_static"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+
+[[package]]
+name = "libc"
+version = "0.2.86"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b7282d924be3275cec7f6756ff4121987bc6481325397dde6ba3e7802b1a8b1c"
+
+[[package]]
+name = "log"
+version = "0.4.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "memchr"
+version = "2.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525"
+
+[[package]]
+name = "once_cell"
+version = "1.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "13bd41f508810a131401606d54ac32a467c97172d74ba7662562ebba5ad07fa0"
+
+[[package]]
+name = "proc-macro-error"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
+dependencies = [
+ "proc-macro-error-attr",
+ "proc-macro2",
+ "quote",
+ "syn",
+ "version_check",
+]
+
+[[package]]
+name = "proc-macro-error-attr"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "version_check",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71"
+dependencies = [
+ "unicode-xid",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "regex"
+version = "1.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d9251239e129e16308e70d853559389de218ac275b515068abc96829d05b948a"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+ "thread_local",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.6.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b5eb417147ba9860a96cfe72a0b93bf88fee1744b5636ec99ab20c1aa9376581"
+
+[[package]]
+name = "rice_stats"
+version = "0.1.0"
+dependencies = [
+ "regex",
+ "structopt",
+ "sudo",
+]
+
+[[package]]
+name = "structopt"
+version = "0.3.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5277acd7ee46e63e5168a80734c9f6ee81b1367a7d8772a2d765df2a3705d28c"
+dependencies = [
+ "clap",
+ "lazy_static",
+ "structopt-derive",
+]
+
+[[package]]
+name = "structopt-derive"
+version = "0.4.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5ba9cdfda491b814720b6b06e0cac513d922fc407582032e8706e9f137976f90"
+dependencies = [
+ "heck",
+ "proc-macro-error",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "sudo"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "88bd84d4c082e18e37fef52c0088e4407dabcef19d23a607fb4b5ee03b7d5b83"
+dependencies = [
+ "libc",
+ "log",
+]
+
+[[package]]
+name = "syn"
+version = "1.0.60"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c700597eca8a5a762beb35753ef6b94df201c81cca676604f547495a0d7f0081"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-xid",
+]
+
+[[package]]
+name = "textwrap"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
+dependencies = [
+ "unicode-width",
+]
+
+[[package]]
+name = "thread_local"
+version = "1.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8018d24e04c95ac8790716a5987d0fec4f8b27249ffa0f7d33f1369bdfb88cbd"
+dependencies = [
+ "once_cell",
+]
+
+[[package]]
+name = "unicode-segmentation"
+version = "1.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bb0d2e7be6ae3a5fa87eed5fb451aff96f2573d2694942e40543ae0bbe19c796"
+
+[[package]]
+name = "unicode-width"
+version = "0.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3"
+
+[[package]]
+name = "unicode-xid"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
+
+[[package]]
+name = "version_check"
+version = "0.9.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed"
A => Cargo.toml +12 -0
@@ 1,12 @@
+[package]
+name = "rice_stats"
+version = "0.1.0"
+authors = ["deadjakk <jack72jill@gmail.com>"]
+edition = "2018"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+structopt = { version = "0.3", default-features = false }
+sudo = {version = "0.6.0" }
+regex = "1"
A => README.md +46 -0
@@ 1,46 @@
+# Rice Status
+## Description
+The goal of rice_stats is to have one cross-compilable binary that can be
+used to retrieve output for 'custom/script' polybar modules to streamline
+polybar configurations across installations.
+Each command will append to the last, so that it can all in one polybar module.
+Alternatively the binary can be run separately for each polybar module, however
+one might prefer...
+
+## Installation
+`sudo cargo install --git https://git.sr.ht/~deadjakk/RiceStats`
+If you plan on using the `-a` (update) function of the binary, it will need
+root privileges to obtain the number of available updates, this is most easily
+achieved by setting the SUID bit on the binary.
+```
+sudo chown root `which rice_stats` && sudo chmod u+s `which rice_stats`
+```
+
+## Help
+```
+rice_stats -h
+rice_stats 0.1.0
+
+USAGE:
+ rice_stats [FLAGS] [OPTIONS]
+
+FLAGS:
+ -a, --apt This flag will cause the program to run `apt update -y && apt upgrade` updating the apt repository
+ but NOT the packages and then output the parsed number of available updates REQUIRES SUID bit to be
+ set on this binary via: sudo chown <this binary> && sudo chmod u+s <this binary> or alternatively
+ this binary can be run as an elevated user
+ -h, --help Prints help information
+ -u, --usage Return the current network usage for the primary interface
+ -V, --version Prints version information
+
+OPTIONS:
+ -i, --interface <interface> Override the interface that is used for several commands. If this is not provided an
+ attempt will be made to find the primary interface based on the presence of an ip and
+ then traffic rate
+```
+
+## Usage
+```
+~$ rice_stats -u -a -i wlp2s0
+0 Updates Available rx-425, tx-7903
+```
A => src/lib.rs +49 -0
@@ 1,49 @@
+pub use std::{fmt,error::Error,process::Command};
+
+#[derive(Debug)]
+pub enum RiceError {
+ SUIDFailed,
+ CommandFailed,
+ InterfaceNotFound,
+}
+
+impl Error for RiceError{}
+impl fmt::Display for RiceError{
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ RiceError::SUIDFailed => {
+ write!(f,"SUID could not be obtained \
+ chown root <this binary> && \
+ sudo chmod u+s this binary and \
+ try again") }
+ RiceError::CommandFailed => {
+ write!(f,"command must have failed")
+ }
+ RiceError::InterfaceNotFound => {
+ write!(f,"interface provided may be invalid")
+ }
+ }
+ }
+}
+
+pub fn run_command(command : &str, output_line: &mut String) -> Result<(),RiceError>{
+ let output = Command::new("sh")
+ .arg("-c")
+ .arg(&command)
+ .output()
+ .expect("failed to execute process");
+
+ match output.status.success() {
+ true => {
+ output_line.push_str(std::str::from_utf8(&output.stdout)
+ .unwrap_or("failed to parse output"));
+ return Ok(());
+ }
+ false => {
+ let err_out = std::str::from_utf8(&output.stderr)
+ .unwrap_or("failed to parse stderr");
+ output_line.push_str(err_out);
+ return Err(RiceError::CommandFailed);
+ }
+ }
+}
A => src/main.rs +50 -0
@@ 1,50 @@
+mod updates_utils;
+mod network;
+use updates_utils::apt_updates;
+use network::usage;
+use structopt::StructOpt;
+
+#[derive(Debug,StructOpt)]
+struct UserOptions {
+
+ #[structopt(short,long)]
+ /// Override the interface that is used for
+ /// several commands.
+ /// If this is not provided an attempt
+ /// will be made to find the primary interface based
+ /// on the presence of an ip and then traffic rate.
+ interface: Option<String>,
+
+ #[structopt(short,long)]
+ /// This flag will cause the program to run `apt update -y && apt upgrade`
+ /// updating the apt repository but NOT the packages
+ /// and then output the parsed number of available updates
+ /// REQUIRES SUID bit to be set on this binary via:
+ /// sudo chown <this binary> && sudo chmod u+s <this binary>
+ /// or alternatively this binary can be run as an elevated user
+ apt: bool,
+
+ #[structopt(short,long)]
+ /// Return the current network usage for the primary interface
+ usage: bool,
+
+}
+
+fn main() {
+ let args = UserOptions::from_args();
+ let mut output_line = String::new();
+
+ if args.usage == true {
+ if let Err(e) = usage(&mut output_line, args.interface){
+ eprintln!("Error getting network usage: {}",e);
+ }
+ }
+
+ if args.apt == true {
+ if let Err(e) = apt_updates(&mut output_line){
+ eprintln!("Error received attempting show_updates: {}",e);
+ }
+ }
+
+ print!("{}",output_line);
+}
A => src/network.rs +85 -0
@@ 1,85 @@
+use rice_stats::{RiceError};
+use std::fs::{read_dir,read};
+use std::error::Error;
+use std::str::{from_utf8};
+use std::path::PathBuf;
+use std::ffi::{OsString};
+use std::time::Duration;
+use std::thread::sleep;
+
+pub fn usage(output_line: &mut String, provided_interface: Option<String>) -> Result<(),Box<dyn Error>> {
+ let interface = match provided_interface {
+ Some(v) => {
+ // Validate that the provided interface exists
+ let int = OsString::from(&v);
+ check_interface(&int)?;
+ int
+ }
+ None => {
+ // No interface was provided, so we need to make
+ // assumptions based on usage
+ let int = find_interface()?;
+ int
+ }
+ };
+ let int_path = PathBuf::from(&interface);
+ let first = get_traffic(&int_path,true)?;
+ sleep(Duration::from_millis(1000));
+ let second = get_traffic(&int_path,true)?;
+ let tx = second-first;
+ let first = get_traffic(&int_path,false)?;
+ sleep(Duration::from_millis(1000));
+ let second = get_traffic(&int_path,false)?;
+ let rx = second-first;
+ output_line.push_str(&format!("rx-{} tx-{} ",rx,tx));
+ return Ok(());
+}
+
+fn get_traffic(path: &PathBuf,tx: bool) -> Result<usize,Box<dyn Error>> {
+ let mut f_path = PathBuf::new();
+ f_path.push("/sys/class/net/");
+ f_path.push(path);
+ f_path.push(r"statistics");
+ if tx == true {
+ f_path.push(r"tx_bytes");
+ } else {
+ f_path.push(r"rx_bytes");
+ }
+
+ let contents = read(f_path)?;
+ let mut contents = from_utf8(&contents)?.to_string();
+ contents.truncate(contents.len()-1);
+ let contents_parsed = contents.parse::<usize>()?;
+ Ok(contents_parsed)
+}
+
+fn check_interface(interface: &OsString) -> Result<(),RiceError> {
+ let mut int_path = PathBuf::from("/sys/class/net/");
+ int_path.push(interface);
+ if let Err(e) = read_dir(int_path){
+ eprintln!("Error reading interface: {}",e.to_string());
+ return Err(RiceError::InterfaceNotFound);
+ }
+
+ Ok(())
+}
+
+fn find_interface() -> Result<OsString,Box<dyn Error>> {
+ let int_path = "/sys/class/net/";
+ let ints = read_dir(int_path)?
+ .map(|res| res.map(|e| e.path()))
+ .collect::<Result<Vec<_>, std::io::Error>>()?;
+
+ let mut max = 0;
+ let mut primary = OsString::from("_");
+ for int in ints {
+ let name = int.file_name().unwrap();
+ let traffic = get_traffic(&int,true)?;
+ if traffic > max {
+ max = traffic;
+ primary = OsString::from(name);
+ }
+ }
+
+ Ok(primary)
+}
A => src/updates_utils.rs +39 -0
@@ 1,39 @@
+use rice_stats::{RiceError,run_command};
+use sudo::{check,RunningAs,escalate_if_needed};
+use regex::Regex;
+
+pub fn apt_updates(output_line: &mut String) -> Result<(),RiceError> {
+ match check() {
+ RunningAs::User => {
+ return Err(RiceError::SUIDFailed);
+ }
+ _ => (),
+ }
+
+ if let Err(e) = escalate_if_needed(){
+ eprintln!("{}",e.to_string());
+ return Err(RiceError::SUIDFailed);
+ }
+
+ let mut command_to_parse = String::new();
+ if let Err(_) = run_command("apt update -y", &mut command_to_parse){
+ eprintln!("erroneous error output: {}",command_to_parse);
+ }
+
+ let pattern = Regex::new("\\d+? packages").unwrap();
+ let p_match = pattern.captures(&command_to_parse);
+ match p_match {
+ Some(v) => {
+ let num_packages: Vec<_> = v[0].split(" ").collect();
+ output_line.push_str(num_packages[0]);
+ output_line.push_str(" Updates Available ");
+ return Ok(());
+ },
+ None => {
+ // Updates were not found
+ output_line.push_str("0 Updates Available ");
+ return Ok(());
+ }
+ }
+}
+