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
//! systemctl: small crate to interact with services through systemctl
//! Homepage: <https://github.com/gwbres/systemctl>
use std::process::ExitStatus;
use std::io::{Read, Error, ErrorKind};
/// calls systemctl $args
fn systemctl (args: Vec<&str>) -> std::io::Result<ExitStatus> {
let mut child = std::process::Command::new("/usr/bin/systemctl")
.args(args)
.stdout(std::process::Stdio::piped())
.spawn()?;
child.wait()
}
/// calls systemctl $args and captures stdout
fn systemctl_capture (args: Vec<&str>) -> std::io::Result<String> {
let mut child = std::process::Command::new("/usr/bin/systemctl")
.args(args)
.stdout(std::process::Stdio::piped())
.spawn()?;
let exitcode = child.wait()?;
match exitcode.success() {
true => {
let mut stdout : Vec<u8> = Vec::new();
if let Ok(size) = child.stdout.unwrap().read_to_end(&mut stdout) {
if size > 0 {
if let Ok(s) = String::from_utf8(stdout) {
Ok(s)
} else {
Err(Error::new(ErrorKind::InvalidData, "Invalid utf8 data in stdout"))
}
} else {
Err(Error::new(ErrorKind::InvalidData, "systemctl stdout empty"))
}
} else {
Err(Error::new(ErrorKind::InvalidData, "systemctl stdout empty"))
}
},
false => {
Err(Error::new(ErrorKind::Other, "systemctl call failed"))
}
}
}
/// Forces given `unit` (re)start
pub fn restart (unit: &str) -> std::io::Result<ExitStatus> { systemctl(vec![unit, "restart"]) }
/// Forces given `unit` to stop
pub fn stop (unit: &str) -> std::io::Result<ExitStatus> { systemctl(vec![unit, "stop"]) }
/// Returns raw status from `systemctl status $unit` call
pub fn status (unit: &str) -> std::io::Result<String> { systemctl_capture(vec![unit, "status"]) }
/// Returns `true` if given `unit` is actively running
pub fn is_active (unit: &str) -> std::io::Result<bool> {
let status = systemctl_capture(vec!["is-active", unit])?;
Ok(status.contains("Active: active (running)"))
}
/// Returns list of units extracted from systemctl listing.
/// + type filter: optionnal --type filter
/// + state filter: optionnal --state filter
fn list_units (type_filter: Option<&str>, state_filter: Option<&str>) -> std::io::Result<Vec<String>> {
let mut args = vec!["list-unit-files"];
if let Some(filter) = type_filter {
args.push("--type");
args.push(filter)
}
if let Some(filter) = state_filter {
args.push("--state");
args.push(filter)
}
let mut result : Vec<String> = Vec::new();
let content = systemctl_capture(args)?;
let lines = content.lines();
for l in lines.skip(1) { // header labels
let parsed : Vec<_> = l.split_ascii_whitespace().collect();
result.push(parsed[0].to_string())
}
Ok(result)
}
/// Returns list of services that are currently declared as disabled
pub fn list_disabled_services() -> std::io::Result<Vec<String>> { Ok(list_units(Some("service"), Some("disabled"))?) }
/// Returns list of services that are currently declared as enabled
pub fn list_enabled_services() -> std::io::Result<Vec<String>> { Ok(list_units(Some("service"), Some("enabled"))?) }
/*
/// `State` describes a Unit State in systemd
pub State {
Static,
Indirect,
Enabled,
Disabled,
}
/// Structure to describe a systemd `unit`
struct Unit {
/// Unit name
name: String,
/// Service script loaded when starting this unit
script: String,
/// Current service status
active: bool,
/// `true` if this unit is automatically started
enabled: bool,
}
impl Default for Unit {
/// Builds a default `Unit` structure
fn default() -> Unit {
Unit {
name: Default::default(),
script: Default::default(),
active: Default::default(),
enabled: Default::default(),
}
}
}
impl Unit {
/// Builds a new descriptor for desired `unit`
pub fn new (name: &str, script: &str, active: bool, enabled: bool) -> Unit {
Unit {
name: name.to_string(),
script: script.to_string(),
active,
enabled,
}
}
/// Builds a new `Unit` structure by retrieving
/// structure attributes with a `systemctl status $unit` call
pub fn from_systemctl (name: &str) -> std::io::Result<Unit> {
let status = status(name)?;
let mut stdout : Vec<u8> = Vec::new();
if let Ok(_) = status.stdout.unwrap().read_to_end(&mut stdout) {
if let Ok(content) = String::from_utf8(stdout) {
let mut lines = content.lines();
let next = lines.next();
let (_, rem) =
Ok(Unit::default())
} else {
Err(Error::new(ErrorKind::InvalidData, "Invalid utf8 data in stdout"))
}
} else {
Err(Error::new(ErrorKind::InvalidData, "systemctl stdout is empty"))
}
}
}
//}
/// ● arp-ethers.service - Load static arp entries
/// Loaded: loaded (/usr/lib/systemd/system/arp-ethers.service; disabled; vendor preset:
/// disabled)
/// Active: inactive (dead)
/// Docs: man:arp(8)
/// man:ethers(5)
///
///╰─$ systemctl status tuned.service
///1 ↵
///● tuned.service - Dynamic System Tuning Daemon
/// Loaded: loaded (/usr/lib/systemd/system/tuned.service; enabled; vendor preset: enabled)
/// Active: active (running) since Fri 2022-03-04 08:29:39 CET; 1 months 8 days ago
/// Docs: man:tuned(8)
/// man:tuned.conf(5)
/// man:tuned-adm(8)
/// Main PID: 1053 (tuned)
/// CGroup: /system.slice/tuned.service
/// └─1053 /usr/bin/python2 -Es /usr/sbin/tuned -l
/// -P
*/