use std::fs::File;
use std::io::prelude::*;
use std::os::fd::AsFd;
use std::os::unix::net::UnixDatagram;
use rustix::fs::fcntl_add_seals;
use rustix::fs::memfd_create;
use rustix::fs::MemfdFlags;
use rustix::fs::SealFlags;
use rustix::io::Errno;
use rustix::net::sendmsg_unix;
use rustix::net::SendAncillaryBuffer;
use rustix::net::SendFlags;
use rustix::net::SocketAddrUnix;
const JOURNALD_PATH: &str = "/run/systemd/journal/socket";
pub struct JournalClient {
socket: UnixDatagram,
}
impl JournalClient {
pub fn new() -> std::io::Result<Self> {
let client = Self {
socket: UnixDatagram::unbound()?,
};
client.send_payload(&[])?;
Ok(client)
}
pub fn send_payload(&self, payload: &[u8]) -> std::io::Result<usize> {
self.socket
.send_to(payload, JOURNALD_PATH)
.or_else(|error| {
if Some(Errno::MSGSIZE) == Errno::from_io_error(&error) {
self.send_large_payload(payload)
} else {
Err(error)
}
})
}
fn send_large_payload(&self, payload: &[u8]) -> std::io::Result<usize> {
let mut mem: File = memfd_create(
"systemd-journal-logger",
MemfdFlags::ALLOW_SEALING | MemfdFlags::CLOEXEC,
)?
.into();
mem.write_all(payload)?;
fcntl_add_seals(
&mem,
SealFlags::SEAL | SealFlags::SHRINK | SealFlags::WRITE | SealFlags::GROW,
)?;
let fds = &[mem.as_fd()];
let scm_rights = rustix::net::SendAncillaryMessage::ScmRights(fds);
let mut buffer = [0; 64];
assert!(
scm_rights.size() <= buffer.len(),
"static buffer size not sufficient for ScmRights message of size {}",
scm_rights.size()
);
let mut buffer = SendAncillaryBuffer::new(&mut buffer);
assert!(buffer.push(scm_rights), "Failed to push ScmRights message");
let size = sendmsg_unix(
&self.socket,
&SocketAddrUnix::new(JOURNALD_PATH)?,
&[],
&mut buffer,
SendFlags::NOSIGNAL,
)?;
Ok(size)
}
}