1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
//! Responsible for setting up IPC/file connections.
//!
//! This is achieved via the main struct IPCServer, documented below.

use crate::parse;
use crate::parse::Interface;
use crate::GetRequestAns;

use std::fs;
use std::io::{BufRead, BufReader};
use std::os::unix::net::{UnixListener, UnixStream};
use std::path::Path;
use std::sync::{Arc, Mutex};
use std::thread;

type Handle =
    fn(&mut Interface, String) -> parse::Result<Option<GetRequestAns<'_>>>;

/// Wrap a UnixListener to appropriately handle IPC and multithreading.
pub struct IPCServer {
    listener: UnixListener,
    interface: Arc<Mutex<Interface>>,
    handler: Arc<Box<Handle>>,
}

impl IPCServer {
    const LOCATION: &'static str = "/tmp/msb-final-ipc-socket";

    /// Initialize the connection
    ///
    /// # Panics
    /// * If the socket path exists and can't be removed
    pub fn new(
        handler: Box<Handle>,
        interface: Interface,
    ) -> std::io::Result<Self> {
        let socket = Path::new(Self::LOCATION);

        if socket.exists() {
            fs::remove_file(&socket).unwrap();
        }

        Ok(Self {
            listener: UnixListener::bind(&socket)?,
            interface: Arc::new(Mutex::new(interface)),
            handler: Arc::new(handler),
        })
    }

    /// Enter the main loop: for every stream connecting, spawn a new thread to
    /// process its commands. This function never returns.
    ///
    /// # Panics
    /// * if opening a stream returns an error
    /// * if accepting a connection returns an error
    ///
    /// # TODOs
    /// Indicate that the function never returns with ! return type. Currently
    /// this causes a compiler error that I'm too lazy to figure out.
    pub fn main_loop(&self) {
        for stream in self.listener.incoming() {
            let handle = self.handler.clone();
            let interface = self.interface.clone();
            thread::spawn(move || {
                Self::handle(stream.unwrap(), handle, interface)
            });
        }
    }

    fn handle(
        conn: UnixStream,
        handler: Arc<Box<Handle>>,
        interface: Arc<Mutex<Interface>>,
    ) {
        let stream = BufReader::new(conn);
        for line in stream.lines() {
            match line {
                Ok(s) => {
                    match (**handler)(&mut interface.clone().lock().unwrap(), s)
                    {
                        Err(e) => println!("{:?}", e),
                        Ok(o) => println!("{:?}", o),
                    };
                }
                Err(_) => {} // TODO good failing
            };
        }
    }
}

impl Drop for IPCServer {
    fn drop(&mut self) {
        fs::remove_file(Self::LOCATION).unwrap();
    }
}