From 48c2945172b88c35c187d298a35bf26716af4e91 Mon Sep 17 00:00:00 2001 From: "Jannis M. Hoffmann" Date: Thu, 21 Nov 2024 21:14:40 +0100 Subject: Switch to varlink as IPC protocol This is a lot! Whole new design on top of a statefult varlink interface. You can now handle multiple request response cycles over a single connection. The error responses are lot more refined than just status codes with optional messages and finally part of the protocol. TODO: A lot of error handling needs to be improved. --- src/main.rs | 128 ++++++++++++++++++++++++++++++++---------------------------- 1 file changed, 69 insertions(+), 59 deletions(-) (limited to 'src/main.rs') diff --git a/src/main.rs b/src/main.rs index 5598b30..a8e33b2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,46 +1,19 @@ -use std::ffi::{CStr, CString}; -use std::io::{stdin, stdout, Read, Write}; +use std::io::{BufRead, BufReader}; +use std::net::Shutdown; +use std::os::fd::FromRawFd; +use std::os::unix::net::UnixStream; +use std::str::FromStr; -use clap::Parser as _; +use varlink::{ConnectionHandler, ErrorKind, Stream, VarlinkService}; -mod arguments; mod cmd; -mod error; -mod pb3; +#[path = "de_jmhoffmann_jwebmail_mail-storage.rs"] +mod de_jmhoffmann_jwebmail_mailstorage; mod rfc822; -use arguments::{Arguments, Mode}; -use error::{Error, Result}; +use cmd::MailStorage; -fn switch_to_user(sys_user: &str) -> Result<()> { - unsafe { - *libc::__errno_location() = 0; - } - let c_sys_user = - CString::new(sys_user).map_err(|e| Error::Setuid(format!("nul char in user, {}", e)))?; - let user_info: *const libc::passwd = unsafe { libc::getpwnam(c_sys_user.as_ptr()) }; - let err = unsafe { *libc::__errno_location() }; - if err != 0 { - return Err(Error::Setuid(format!( - "error calling getpwnam {:?}", - unsafe { libc::strerror(err) } - ))); - }; - if user_info.is_null() { - return Err(Error::Setuid(format!("user {:?} does not exist", sys_user))); - }; - let rc = unsafe { libc::setuid((*user_info).pw_uid) }; - if rc != 0 { - let err = unsafe { *libc::__errno_location() }; - return Err(Error::Setuid(format!( - "error calling setuid {:?}", - unsafe { CStr::from_ptr(libc::strerror(err)) } - ))); - } - Ok(()) -} - -fn main() -> Result<()> { +fn main() { simplelog::TermLogger::init( simplelog::LevelFilter::Info, simplelog::Config::default(), @@ -49,29 +22,66 @@ fn main() -> Result<()> { ) .unwrap(); - let args = Arguments::parse(); - - switch_to_user(&args.sys_user)?; + let mail_storage = MailStorage::default(); - let path = args.maildir_path.join(args.mail_user); + let myinterface = de_jmhoffmann_jwebmail_mailstorage::new(Box::new(mail_storage)); + let service = VarlinkService::new( + "de.jmhoffmann.jwebmail.mail-storage.varlink", + "jwebmails storage service", + "1.0.1", + "https://fehcom.de/cgit/jwebmail2/", + vec![Box::new(myinterface)], + ); - stdout().write_all(b"OPEN\n")?; - stdout().flush()?; - let mut req = Vec::with_capacity(2048); - stdin().read_to_end(&mut req)?; - - let res = match args.mode { - Mode::Read => cmd::read(path, &req), - Mode::Raw => cmd::raw(path, &req), - Mode::List => cmd::list(path, &req), - Mode::Folders => cmd::folders(path, &req), - Mode::Count => cmd::count(path, &req), - //Mode::Search => cmd::search(&path, &req), - Mode::Move => cmd::move_mail(path, &req), - Mode::Remove => cmd::remove(path, &req), - Mode::AddFolder => cmd::add_folder(path, &req), - _ => todo!(), - }?; + if let Ok(listen_fds) = std::env::var("LISTEN_FDS") { + let lfds = u8::from_str(&listen_fds).expect("env variable `LISTEN_FDS` must be an integer"); + if lfds < 1 { + log::error!("No file descriptor to receive commands from!"); + return; + } else if lfds == 1 { + let uds = unsafe { UnixStream::from_raw_fd(3) }; + accept_con(&service, uds); + } else { + let listen_fdnames = std::env::var("LISTEN_FDNAMES") + .expect("when multiple `LISTEN_FDS` are available `LISTEN_FDNAMES` must be set"); + listen_fdnames + .split(':') + .enumerate() + .filter(|(_, name)| *name == "varlink") + .for_each(|(i, _)| { + accept_con(&service, unsafe { UnixStream::from_raw_fd(3 + i as i32) }) + }); + } + } else { + log::error!("No file descriptor to receive commands from!"); + } +} - stdout().write_all(&res).map_err(|e| e.into()) +/// mostly a copy from varlink::listen +fn accept_con(service: &VarlinkService, mut uds: UnixStream) { + let (r, mut w) = uds.split().unwrap(); + let mut br = BufReader::new(r); + let mut iface: Option = None; + loop { + match service.handle(&mut br, &mut w, iface.clone()) { + Ok((_, i)) => { + iface = i; + match br.fill_buf() { + Err(_) => break, + Ok(buf) if buf.is_empty() => break, + _ => {} + } + } + Err(err) => { + match err.kind() { + ErrorKind::ConnectionClosed | ErrorKind::SerdeJsonDe(_) => {} + _ => { + eprintln!("Worker error: {:?}", err); + } + } + let _ = uds.shutdown(Shutdown::Both); + break; + } + } + } } -- cgit v1.2.3