summaryrefslogtreecommitdiff
path: root/src/main.rs
diff options
context:
space:
mode:
authorJannis M. Hoffmann <jannis@fehcom.de>2024-11-21 21:14:40 +0100
committerJannis M. Hoffmann <jannis@fehcom.de>2024-11-21 21:14:40 +0100
commit48c2945172b88c35c187d298a35bf26716af4e91 (patch)
tree2af21ddb4dcacd191e07fef156609b7c1488ebaf /src/main.rs
parent6ed535387df0dffa72a10e601b8ea37c99345d84 (diff)
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.
Diffstat (limited to 'src/main.rs')
-rw-r--r--src/main.rs128
1 files changed, 69 insertions, 59 deletions
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<String> = 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;
+ }
+ }
+ }
}