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
//! Utitilty for writing nagios-style check scripts/plugins
//!
//! There are three things:
//!
//! * The `Status` enum for representing health status
//! * The `procfs` module, which contains rusty representations of some files
//!   from /proc
//! * A few scripts in the bin directory, which contain actual
//!   nagios-compatible scripts
//!
//! TODOs include
//!
//! * Nice logging, including some standard formats -- json and human would be
//!   nice
//! * Some way of easily standardizing command-line args
//! * Much of the code is hideous, and should not be

#[macro_use]
extern crate derive_more;
#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate scan_fmt;

extern crate libc;
extern crate nix;
extern crate regex;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;

use std::process;
use std::fmt;
use std::str::FromStr;

pub mod linux;
pub mod procfs;
pub mod sys;
pub mod scripts;

/// All errors are TabinErrors
#[derive(Debug)]
pub enum TabinError {
    /// Represents an incorrect value passed in to a function
    UnknownValue(String),
}

impl fmt::Display for TabinError {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        match self {
            &TabinError::UnknownValue(ref msg) => write!(f, "Unknown Value: {}", msg),
        }
    }
}

/// All results are TabinResults
pub type TabinResult<T> = Result<T, TabinError>;

/// Represent the nagios-ish error status of a script.
#[must_use]
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Deserialize)]
pub enum Status {
    /// Unexpected result
    Unknown,
    /// Everything is fine
    Ok,
    /// Something is weird, but don't necessary call anybody
    Warning,
    /// OMG CALL SOMEONE I DON'T CARE WHAT TIME IT IS
    Critical,
}

impl Status {
    /// Exit with a return code that indicates the state of the system
    pub fn exit(&self) -> ! {
        use self::Status::*;
        match *self {
            Ok => process::exit(0),
            Warning => process::exit(1),
            Critical => process::exit(2),
            Unknown => process::exit(3),
        }
    }

    /// The legal values for `from_str`
    pub fn str_values() -> [&'static str; 4] {
        ["ok", "warning", "critical", "unknown"]
    }
}

impl FromStr for Status {
    type Err = TabinError;

    /// Primarily useful to construct from argv
    fn from_str(s: &str) -> TabinResult<Status> {
        use Status::{Critical, Unknown, Warning};
        match s {
            "ok" => Ok(Status::Ok),
            "warning" => Ok(Warning),
            "critical" => Ok(Critical),
            "unknown" => Ok(Unknown),
            _ => Err(TabinError::UnknownValue(format!(
                "Unexpected exit status: {}",
                s
            ))),
        }
    }
}

impl fmt::Display for Status {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use Status::*;
        let msg = match *self {
            Ok => "OK",
            Unknown => "UNKNOWN",
            Warning => "WARNING",
            Critical => "CRITICAL",
        };
        write!(f, "{}", msg)
    }
}

#[test]
fn comparison_is_as_expected() {
    use Status::*;
    assert!(Ok < Critical);
    assert!(Ok < Warning);
    assert_eq!(std::cmp::max(Warning, Critical), Critical)
}