bot: watch journald and send events

This commit is contained in:
Antoine Martin 2021-02-07 19:02:24 +01:00
parent 813ea9848a
commit dc85ff17e5
4 changed files with 184 additions and 20 deletions

119
Cargo.lock generated
View file

@ -130,9 +130,11 @@ version = "0.1.0"
dependencies = [
"anyhow",
"clap",
"futures",
"matrix-sdk",
"serde",
"serde_yaml",
"systemd",
"thiserror",
"tokio",
"tracing-subscriber",
@ -166,6 +168,12 @@ dependencies = [
"generic-array",
]
[[package]]
name = "build-env"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6cf89846ef2b2674ef1c153256cec98fba587c72bf4ea2c4b2f6d91a19f55926"
[[package]]
name = "bumpalo"
version = "3.6.0"
@ -190,6 +198,12 @@ version = "1.0.66"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c0496836a84f8d0495758516b8621a622beb77c0fed418570e50764093ced48"
[[package]]
name = "cfg-if"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
[[package]]
name = "cfg-if"
version = "1.0.0"
@ -322,7 +336,7 @@ version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
]
[[package]]
@ -331,7 +345,7 @@ version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1aaa739f95311c2c7887a76863f500026092fb1dce0161dab577e559ef3569d"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"const_fn",
"crossbeam-utils",
"lazy_static",
@ -346,7 +360,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02d96d1e189ef58269ebe5b97953da3274d83a93af647c2ddd6f9dab28cedb8d"
dependencies = [
"autocfg",
"cfg-if",
"cfg-if 1.0.0",
"lazy_static",
]
@ -360,6 +374,16 @@ dependencies = [
"subtle",
]
[[package]]
name = "cstr-argument"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "20bd4e8067c20c7c3a4dea759ef91d4b18418ddb5bd8837ef6e2f2f93ca7ccbb"
dependencies = [
"cfg-if 0.1.10",
"memchr",
]
[[package]]
name = "ctr"
version = "0.6.0"
@ -375,7 +399,7 @@ version = "4.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e77a43b28d0668df09411cb0bc9a8c2adc40f9a048afe863e05fd43251e8e39c"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"num_cpus",
]
@ -406,7 +430,7 @@ version = "0.8.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "801bbab217d7f79c0062f4f7205b5d4427c6d1a7bd7aafdd1475f7c59d62b283"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
]
[[package]]
@ -421,7 +445,28 @@ version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
dependencies = [
"foreign-types-shared",
"foreign-types-shared 0.1.1",
]
[[package]]
name = "foreign-types"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965"
dependencies = [
"foreign-types-macros",
"foreign-types-shared 0.3.0",
]
[[package]]
name = "foreign-types-macros"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "63f713f8b2aa9e24fec85b0e290c56caee12e3b6ae0aeeda238a75b28251afd6"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
@ -430,6 +475,12 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
[[package]]
name = "foreign-types-shared"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7684cf33bb7f28497939e8c7cf17e3e4e3b8d9a0080ffa4f8ae2f515442ee855"
[[package]]
name = "form_urlencoded"
version = "1.0.0"
@ -589,7 +640,7 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c9495705279e7140bf035dde1f6e750c162df8b625267cd52cc44e0b156732c8"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"js-sys",
"libc",
"wasi",
@ -770,7 +821,7 @@ version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61124eeebbd69b8190558df225adf7e4caafce0d743919e5d6b19652314ec5ec"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"js-sys",
"time 0.2.25",
"wasm-bindgen",
@ -819,6 +870,17 @@ version = "0.2.84"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1cca32fa0182e8c0989459524dc356b8f2b5c10f1b9eb521b7d182c03cf8c5ff"
[[package]]
name = "libsystemd-sys"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e03fd580bcecda68dcdcd5297085ade6a3dc552cd8b030d2b94a9b089ef7ab8"
dependencies = [
"build-env",
"libc",
"pkg-config",
]
[[package]]
name = "linked-hash-map"
version = "0.5.4"
@ -840,7 +902,7 @@ version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
]
[[package]]
@ -1089,8 +1151,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "038d43985d1ddca7a9900630d8cd031b56e4794eecc2e9ea39dd17aa04399a70"
dependencies = [
"bitflags",
"cfg-if",
"foreign-types",
"cfg-if 1.0.0",
"foreign-types 0.3.2",
"lazy_static",
"libc",
"openssl-sys",
@ -1138,7 +1200,7 @@ version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ccb628cad4f84851442432c60ad8e1f607e29752d0bf072cbd0baf28aa34272"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"instant",
"libc",
"redox_syscall 0.1.57",
@ -1748,7 +1810,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa827a14b29ab7f44778d14a88d3cb76e949c45083f7dbfa507d0cb699dc12de"
dependencies = [
"block-buffer",
"cfg-if",
"cfg-if 1.0.0",
"cpuid-bool 0.1.2",
"digest",
"opaque-debug",
@ -1806,7 +1868,7 @@ version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "122e570113d28d773067fab24266b66753f6ea915758651696b6e35e49f88d6e"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"libc",
"winapi",
]
@ -1904,13 +1966,28 @@ dependencies = [
"unicode-xid",
]
[[package]]
name = "systemd"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f722cabda922e471742300045f56dbaa53fafbb4520fca304e51258019bfe91d"
dependencies = [
"cstr-argument",
"foreign-types 0.5.0",
"libc",
"libsystemd-sys",
"log",
"memchr",
"utf8-cstr",
]
[[package]]
name = "tempfile"
version = "3.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"libc",
"rand",
"redox_syscall 0.2.4",
@ -2104,7 +2181,7 @@ version = "0.1.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f47026cdc4080c07e49b37087de021820269d996f581aac150ef9e5583eefe3"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"pin-project-lite",
"tracing-attributes",
"tracing-core",
@ -2254,6 +2331,12 @@ dependencies = [
"serde",
]
[[package]]
name = "utf8-cstr"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55bcbb425141152b10d5693095950b51c3745d019363fc2929ffd8f61449b628"
[[package]]
name = "uuid"
version = "0.8.2"
@ -2304,7 +2387,7 @@ version = "0.2.70"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55c0f7123de74f0dab9b7d00fd614e7b19349cd1e2f5252bbe9b1754b59433be"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"serde",
"serde_json",
"wasm-bindgen-macro",
@ -2331,7 +2414,7 @@ version = "0.4.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3de431a2910c86679c34283a33f66f4e4abd7e0aec27b6669060148872aadf94"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"js-sys",
"wasm-bindgen",
"web-sys",

View file

@ -9,11 +9,13 @@ edition = "2018"
[dependencies]
anyhow = "1.0"
clap = "3.0.0-beta.2"
futures = "0.3"
tokio = { version = "1", features = [ "full" ] }
tracing-subscriber = "0.2"
url = { version = "2.2", features = [ "serde" ] }
serde_yaml = "0.8"
serde = "1.0"
systemd = "0.8"
thiserror = "1.0"
[dependencies.matrix-sdk]

View file

@ -3,11 +3,19 @@ use std::{
io::{BufReader, BufWriter},
};
use matrix_sdk::{Client, ClientConfig, Session, SyncSettings};
use matrix_sdk::{
events::{
room::message::{MessageEventContent, TextMessageEventContent},
AnyMessageEventContent,
},
Client, ClientConfig, Session, SyncSettings,
};
use systemd::{journal, JournalRecord};
use crate::autojoin::AutoJoinHandler;
use crate::Config;
#[derive(Clone)]
pub struct BadNewsBot {
client: Client,
config: Config,
@ -46,8 +54,76 @@ impl BadNewsBot {
/// [`BadNewsBot::init`] **must** be called before this function, otherwise the [`Client`] isn't
/// logged in.
pub async fn run(&self) {
let clone = self.clone();
tokio::task::spawn_blocking(move || clone.watch_journald());
self.client.sync(SyncSettings::default()).await
}
fn watch_journald(&self) {
let mut reader = journal::OpenOptions::default()
.system(true)
.open()
.expect("Could not open journal");
// Seek to end of current log to prevent old messages from being printed
reader
.seek_tail()
.expect("Could not seek to end of journal");
// HACK: for some reason calling `seek_tail` above still leaves old entries when calling
// next, so skip all those before we start the real logging
loop {
if reader.next().unwrap() == 0 {
break;
}
}
// NOTE: Ugly double loop, but low level `wait` has to be used if we don't want to miss any
// new entry. See https://github.com/jmesmon/rust-systemd/issues/66
loop {
loop {
let record = reader.next_entry().unwrap();
match record {
Some(record) => self.handle_record(record),
None => break,
}
}
reader.wait(None).unwrap();
}
}
fn handle_record(&self, record: JournalRecord) {
const KEY_UNIT: &str = "_SYSTEMD_UNIT";
const KEY_MESSAGE: &str = "MESSAGE";
if let Some(unit) = record.get(KEY_UNIT) {
if !self.config.units.contains(unit) {
return;
}
let message = record.get(KEY_MESSAGE);
let message = format!(
"[{}] {}",
unit.strip_suffix(".service").unwrap_or(unit),
message.map(|m| m.as_ref()).unwrap_or("<EMPTY MESSAGE>")
);
let content = AnyMessageEventContent::RoomMessage(MessageEventContent::Text(
TextMessageEventContent::plain(message),
));
let room_id = self.config.room_id.clone();
let client_clone = self.client.clone();
tokio::spawn(async move {
client_clone
.room_send(&room_id, content, None)
.await
.unwrap();
});
}
}
}
/// This loads the session information from an existing file, and tries to login with it. If no such

View file

@ -1,4 +1,5 @@
use std::{
collections::HashSet,
fs::File,
io::{self, BufReader},
path::PathBuf,
@ -32,7 +33,7 @@ struct Opts {
}
/// Holds the configuration for the bot.
#[derive(Deserialize)]
#[derive(Clone, Deserialize)]
pub struct Config {
/// The URL for the homeserver we should connect to
homeserver: Url,
@ -45,6 +46,8 @@ pub struct Config {
/// ID of the Matrix room where the bot should post messages. The bot will only accept
/// invitations to this room.
room_id: RoomId,
/// Units to watch for logs
units: HashSet<String>,
}
#[tokio::main]