use std::fs::File;
use std::io::prelude::*;
use std::mem::MaybeUninit;
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_addr;
use rustix::net::SendAncillaryBuffer;
use rustix::net::SendAncillaryMessage;
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 mut buffer = [MaybeUninit::uninit(); rustix::cmsg_space!(ScmRights(1))];
let mut buffer = SendAncillaryBuffer::new(&mut buffer);
let fds = &[mem.as_fd()];
assert!(
buffer.push(SendAncillaryMessage::ScmRights(fds)),
"Failed to push ScmRights message"
);
let size = sendmsg_addr(
&self.socket,
&SocketAddrUnix::new(JOURNALD_PATH)?,
&[],
&mut buffer,
SendFlags::NOSIGNAL,
)?;
Ok(size)
}
}